mirror of
https://github.com/go-gitea/gitea.git
synced 2025-12-22 00:45:48 +08:00
rebase on master
This commit is contained in:
parent
0615b668dc
commit
bb96349d09
@ -254,6 +254,8 @@ var migrations = []Migration{
|
|||||||
NewMigration("code comment replies should have the commitID of the review they are replying to", updateCodeCommentReplies),
|
NewMigration("code comment replies should have the commitID of the review they are replying to", updateCodeCommentReplies),
|
||||||
// v159 -> v160
|
// v159 -> v160
|
||||||
NewMigration("update reactions constraint", updateReactionConstraint),
|
NewMigration("update reactions constraint", updateReactionConstraint),
|
||||||
|
// v160 -> v161
|
||||||
|
NewMigration("add size limit on repository", addSizeLimitOnRepo),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|||||||
16
models/migrations/v160.go
Normal file
16
models/migrations/v160.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync2(new(Repository))
|
||||||
|
}
|
||||||
@ -230,6 +230,7 @@ type Repository struct {
|
|||||||
TemplateID int64 `xorm:"INDEX"`
|
TemplateID int64 `xorm:"INDEX"`
|
||||||
TemplateRepo *Repository `xorm:"-"`
|
TemplateRepo *Repository `xorm:"-"`
|
||||||
Size int64 `xorm:"NOT NULL DEFAULT 0"`
|
Size int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||||
|
SizeLimit int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||||
CodeIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
CodeIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
||||||
StatsIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
StatsIndexerStatus *RepoIndexerStatus `xorm:"-"`
|
||||||
IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
|
IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
|
||||||
@ -860,20 +861,29 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
|
|||||||
return repo.OwnerID == userID
|
return repo.OwnerID == userID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) updateSize(e Engine) error {
|
func (repo *Repository) computeSize() (int64, error) {
|
||||||
size, err := util.GetDirectorySize(repo.RepoPath())
|
size, err := util.GetDirectorySize(repo.RepoPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("updateSize: %v", err)
|
return 0, fmt.Errorf("computeSize: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
objs, err := repo.GetLFSMetaObjects(-1, 0)
|
objs, err := repo.GetLFSMetaObjects(-1, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("updateSize: GetLFSMetaObjects: %v", err)
|
return 0, fmt.Errorf("computeSize: GetLFSMetaObjects: %v", err)
|
||||||
}
|
}
|
||||||
for _, obj := range objs {
|
for _, obj := range objs {
|
||||||
size += obj.Size
|
size += obj.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) updateSize(e Engine) error {
|
||||||
|
size, err := repo.computeSize()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("updateSize: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
repo.Size = size
|
repo.Size = size
|
||||||
_, err = e.ID(repo.ID).Cols("size").Update(repo)
|
_, err = e.ID(repo.ID).Cols("size").Update(repo)
|
||||||
return err
|
return err
|
||||||
@ -884,6 +894,11 @@ func (repo *Repository) UpdateSize(ctx DBContext) error {
|
|||||||
return repo.updateSize(ctx.e)
|
return repo.updateSize(ctx.e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepoSizeIsOversized return if is over size limitation
|
||||||
|
func (repo *Repository) RepoSizeIsOversized(additionalSize int64) bool {
|
||||||
|
return repo.SizeLimit > 0 && repo.Size+additionalSize > repo.SizeLimit
|
||||||
|
}
|
||||||
|
|
||||||
// CanUserFork returns true if specified user can fork repository.
|
// CanUserFork returns true if specified user can fork repository.
|
||||||
func (repo *Repository) CanUserFork(user *User) (bool, error) {
|
func (repo *Repository) CanUserFork(user *User) (bool, error) {
|
||||||
if user == nil {
|
if user == nil {
|
||||||
@ -1101,6 +1116,7 @@ type CreateRepoOptions struct {
|
|||||||
AutoInit bool
|
AutoInit bool
|
||||||
Status RepositoryStatus
|
Status RepositoryStatus
|
||||||
TrustModel TrustModelType
|
TrustModel TrustModelType
|
||||||
|
SizeLimit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepoInitFile returns repository init files
|
// GetRepoInitFile returns repository init files
|
||||||
|
|||||||
@ -48,6 +48,7 @@ type CreateRepoForm struct {
|
|||||||
Avatar bool
|
Avatar bool
|
||||||
Labels bool
|
Labels bool
|
||||||
TrustModel string
|
TrustModel string
|
||||||
|
SizeLimit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
@ -126,6 +127,7 @@ type RepoSettingForm struct {
|
|||||||
Private bool
|
Private bool
|
||||||
Template bool
|
Template bool
|
||||||
EnablePrune bool
|
EnablePrune bool
|
||||||
|
RepoSizeLimit int64
|
||||||
|
|
||||||
// Advanced settings
|
// Advanced settings
|
||||||
EnableWiki bool
|
EnableWiki bool
|
||||||
|
|||||||
@ -352,8 +352,13 @@ const (
|
|||||||
|
|
||||||
// CountObjects returns the results of git count-objects on the repoPath
|
// CountObjects returns the results of git count-objects on the repoPath
|
||||||
func CountObjects(repoPath string) (*CountObject, error) {
|
func CountObjects(repoPath string) (*CountObject, error) {
|
||||||
|
return CountObjectsWithEnv(repoPath, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountObjectsWithEnv returns the results of git count-objects on the repoPath with custom env setup
|
||||||
|
func CountObjectsWithEnv(repoPath string, env []string) (*CountObject, error) {
|
||||||
cmd := NewCommand("count-objects", "-v")
|
cmd := NewCommand("count-objects", "-v")
|
||||||
stdout, err := cmd.RunInDir(repoPath)
|
stdout, err := cmd.RunInDirWithEnv(repoPath, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mod
|
|||||||
Status: opts.Status,
|
Status: opts.Status,
|
||||||
IsEmpty: !opts.AutoInit,
|
IsEmpty: !opts.AutoInit,
|
||||||
TrustModel: opts.TrustModel,
|
TrustModel: opts.TrustModel,
|
||||||
|
SizeLimit: opts.SizeLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.WithTx(func(ctx models.DBContext) error {
|
if err := models.WithTx(func(ctx models.DBContext) error {
|
||||||
|
|||||||
@ -122,6 +122,8 @@ type CreateRepoOption struct {
|
|||||||
// TrustModel of the repository
|
// TrustModel of the repository
|
||||||
// enum: default,collaborator,committer,collaboratorcommitter
|
// enum: default,collaborator,committer,collaboratorcommitter
|
||||||
TrustModel string `json:"trust_model"`
|
TrustModel string `json:"trust_model"`
|
||||||
|
// SizeLimit of the repository
|
||||||
|
SizeLimit int64 `json:"size_limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditRepoOption options when editing a repository's properties
|
// EditRepoOption options when editing a repository's properties
|
||||||
@ -168,6 +170,8 @@ type EditRepoOption struct {
|
|||||||
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
|
||||||
// set to `true` to archive this repository.
|
// set to `true` to archive this repository.
|
||||||
Archived *bool `json:"archived,omitempty"`
|
Archived *bool `json:"archived,omitempty"`
|
||||||
|
// SizeLimit of the repository.
|
||||||
|
SizeLimit *int64 `json:"size_limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBranchRepoOption options when creating a branch in a repository
|
// CreateBranchRepoOption options when creating a branch in a repository
|
||||||
|
|||||||
@ -653,6 +653,7 @@ owner = Owner
|
|||||||
repo_name = Repository Name
|
repo_name = Repository Name
|
||||||
repo_name_helper = Good repository names use short, memorable and unique keywords.
|
repo_name_helper = Good repository names use short, memorable and unique keywords.
|
||||||
repo_size = Repository Size
|
repo_size = Repository Size
|
||||||
|
repo_size_limit = Repository Size Limit
|
||||||
template = Template
|
template = Template
|
||||||
template_select = Select a template.
|
template_select = Select a template.
|
||||||
template_helper = Make repository a template
|
template_helper = Make repository a template
|
||||||
@ -733,6 +734,8 @@ archive.pull.nocomment = This repo is archived. You cannot comment on pull reque
|
|||||||
form.reach_limit_of_creation = You have already reached your limit of %d repositories.
|
form.reach_limit_of_creation = You have already reached your limit of %d repositories.
|
||||||
form.name_reserved = The repository name '%s' is reserved.
|
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.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.repo_size_limit_only_by_admins = Only administrators can change the repository size limitation.
|
||||||
|
|
||||||
need_auth = Clone Authorization
|
need_auth = Clone Authorization
|
||||||
migrate_options = Migration Options
|
migrate_options = Migration Options
|
||||||
|
|||||||
@ -246,6 +246,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR
|
|||||||
DefaultBranch: opt.DefaultBranch,
|
DefaultBranch: opt.DefaultBranch,
|
||||||
TrustModel: models.ToTrustModel(opt.TrustModel),
|
TrustModel: models.ToTrustModel(opt.TrustModel),
|
||||||
IsTemplate: opt.Template,
|
IsTemplate: opt.Template,
|
||||||
|
SizeLimit: opt.SizeLimit,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrRepoAlreadyExist(err) {
|
if models.IsErrRepoAlreadyExist(err) {
|
||||||
@ -568,6 +569,9 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
|||||||
repo.DefaultBranch = *opts.DefaultBranch
|
repo.DefaultBranch = *opts.DefaultBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.SizeLimit != nil {
|
||||||
|
repo.SizeLimit = *opts.SizeLimit
|
||||||
|
}
|
||||||
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -155,12 +155,33 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
|
|||||||
private.GitQuarantinePath+"="+opts.GitQuarantinePath)
|
private.GitQuarantinePath+"="+opts.GitQuarantinePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pushSize, err := git.CountObjectsWithEnv(repo.RepoPath(), env)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to get repository size with env %v: %s Error: %v", repo.RepoPath(), env, err)
|
||||||
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
"err": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Trace("Push size %d", pushSize.Size)
|
||||||
|
|
||||||
// Iterate across the provided old commit IDs
|
// Iterate across the provided old commit IDs
|
||||||
for i := range opts.OldCommitIDs {
|
for i := range opts.OldCommitIDs {
|
||||||
oldCommitID := opts.OldCommitIDs[i]
|
oldCommitID := opts.OldCommitIDs[i]
|
||||||
newCommitID := opts.NewCommitIDs[i]
|
newCommitID := opts.NewCommitIDs[i]
|
||||||
refFullName := opts.RefFullNames[i]
|
refFullName := opts.RefFullNames[i]
|
||||||
|
|
||||||
|
//Check size
|
||||||
|
if newCommitID != git.EmptySHA && repo.RepoSizeIsOversized(pushSize.Size) { //Check next size if we are not deleting a reference
|
||||||
|
log.Warn("Forbidden: new repo size is over limitation: %d", repo.SizeLimit)
|
||||||
|
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||||
|
"err": fmt.Sprintf("new repo size is over limitation: %d", repo.SizeLimit),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//TODO investigate why on force push some git objects are not cleaned on server side.
|
||||||
|
//TODO corner-case force push and branch creation -> git.EmptySHA == oldCommitID
|
||||||
|
//TODO calculate pushed LFS objects size
|
||||||
|
|
||||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||||
if branchName == repo.DefaultBranch && newCommitID == git.EmptySHA {
|
if branchName == repo.DefaultBranch && newCommitID == git.EmptySHA {
|
||||||
log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo)
|
log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo)
|
||||||
|
|||||||
@ -250,6 +250,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
|
|||||||
AutoInit: form.AutoInit,
|
AutoInit: form.AutoInit,
|
||||||
IsTemplate: form.Template,
|
IsTemplate: form.Template,
|
||||||
TrustModel: models.ToTrustModel(form.TrustModel),
|
TrustModel: models.ToTrustModel(form.TrustModel),
|
||||||
|
SizeLimit: form.SizeLimit,
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
|
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
|
||||||
|
|||||||
@ -50,6 +50,7 @@ func Settings(ctx *context.Context) {
|
|||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
ctx.Data["PageIsSettingsOptions"] = true
|
ctx.Data["PageIsSettingsOptions"] = true
|
||||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
||||||
|
ctx.Data["Err_RepoSize"] = ctx.Repo.Repository.RepoSizeIsOversized(ctx.Repo.Repository.SizeLimit / 10) // less than 10% left
|
||||||
|
|
||||||
signing, _ := models.SigningKey(ctx.Repo.Repository.RepoPath())
|
signing, _ := models.SigningKey(ctx.Repo.Repository.RepoPath())
|
||||||
ctx.Data["SigningKeyAvailable"] = len(signing) > 0
|
ctx.Data["SigningKeyAvailable"] = len(signing) > 0
|
||||||
@ -64,6 +65,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||||||
ctx.Data["PageIsSettingsOptions"] = true
|
ctx.Data["PageIsSettingsOptions"] = true
|
||||||
|
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
ctx.Data["Err_RepoSize"] = repo.RepoSizeIsOversized(repo.SizeLimit / 10) // less than 10% left
|
||||||
|
|
||||||
switch ctx.Query("action") {
|
switch ctx.Query("action") {
|
||||||
case "update":
|
case "update":
|
||||||
@ -128,6 +130,19 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.RepoSizeLimit < 0 {
|
||||||
|
ctx.Data["Err_RepoSizeLimit"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.form.repo_size_limit_negative"), tplSettingsOptions, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.User.IsAdmin && repo.SizeLimit != form.RepoSizeLimit {
|
||||||
|
ctx.Data["Err_RepoSizeLimit"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("repo.form.repo_size_limit_only_by_admins"), tplSettingsOptions, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
repo.SizeLimit = form.RepoSizeLimit
|
||||||
|
|
||||||
repo.IsPrivate = form.Private
|
repo.IsPrivate = form.Private
|
||||||
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
||||||
ctx.ServerError("UpdateRepository", err)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
|
|||||||
@ -17,7 +17,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{.i18n.Tr "repo.repo_size"}}</label>
|
<label>{{.i18n.Tr "repo.repo_size"}}</label>
|
||||||
<span>{{SizeFmt .Repository.Size}}</span>
|
<span {{if .Err_RepoSize}}class="ui text red"{{end}}>{{SizeFmt .Repository.Size}}{{if .Repository.SizeLimit}}/{{SizeFmt .Repository.SizeLimit}}{{end}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if .Err_RepoSizeLimit}}error{{end}}" {{if not .IsAdmin}}style="display:none;"{{end}}>
|
||||||
|
<label for="repo_size_limit">{{.i18n.Tr "repo.repo_size_limit"}}</label>
|
||||||
|
<input id="repo_size_limit" name="repo_size_limit" type="number" value="{{.Repository.SizeLimit}}" data-repo-size-limit="{{.Repository.SizeLimit}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="inline field">
|
<div class="inline field">
|
||||||
<label>{{.i18n.Tr "repo.template"}}</label>
|
<label>{{.i18n.Tr "repo.template"}}</label>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user