Repo size in admin panel (#1482)
* Implementation of the feature to view repository size in admin panel * Move GetRepoSize to git module * Repository.RepoSize -> Repository.Size * RepoSize -> Size in template * Redo a few bits and pieces * Update size when syncing mirror or forking * Remove GetRepoSize * Changed fatal errors to error message * Copy migration code from Gogs * make fmt
This commit is contained in:
		
							parent
							
								
									54f0293f0a
								
							
						
					
					
						commit
						be6edaddcb
					
				| @ -104,6 +104,8 @@ var migrations = []Migration{ | |||||||
| 	NewMigration("generate and migrate repo and wiki Git hooks", generateAndMigrateGitHookChains), | 	NewMigration("generate and migrate repo and wiki Git hooks", generateAndMigrateGitHookChains), | ||||||
| 	// v27 -> v28
 | 	// v27 -> v28
 | ||||||
| 	NewMigration("change mirror interval from hours to time.Duration", convertIntervalToDuration), | 	NewMigration("change mirror interval from hours to time.Duration", convertIntervalToDuration), | ||||||
|  | 	// v28 -> v29
 | ||||||
|  | 	NewMigration("add field for repo size", addRepoSize), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Migrate database to current version
 | // Migrate database to current version
 | ||||||
|  | |||||||
							
								
								
									
										77
									
								
								models/migrations/v28.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								models/migrations/v28.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | // Copyright 2017 The Gogs Authors. All rights reserved.
 | ||||||
|  | // Copyright 2017 Gitea. 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 ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/git" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-xorm/xorm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func addRepoSize(x *xorm.Engine) (err error) { | ||||||
|  | 	log.Info("This migration could take up to minutes, please be patient.") | ||||||
|  | 	type Repository struct { | ||||||
|  | 		ID      int64 | ||||||
|  | 		OwnerID int64 | ||||||
|  | 		Name    string | ||||||
|  | 		Size    int64 | ||||||
|  | 	} | ||||||
|  | 	type User struct { | ||||||
|  | 		ID   int64 | ||||||
|  | 		Name string | ||||||
|  | 	} | ||||||
|  | 	if err = x.Sync2(new(Repository)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// For the sake of SQLite3, we can't use x.Iterate here.
 | ||||||
|  | 	offset := 0 | ||||||
|  | 	for { | ||||||
|  | 		repos := make([]*Repository, 0, 10) | ||||||
|  | 		if err = x.Sql(fmt.Sprintf("SELECT * FROM `repository` ORDER BY id ASC LIMIT 10 OFFSET %d", offset)). | ||||||
|  | 			Find(&repos); err != nil { | ||||||
|  | 			return fmt.Errorf("select repos [offset: %d]: %v", offset, err) | ||||||
|  | 		} | ||||||
|  | 		log.Trace("Select [offset: %d, repos: %d]", offset, len(repos)) | ||||||
|  | 		if len(repos) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		offset += 10 | ||||||
|  | 
 | ||||||
|  | 		for _, repo := range repos { | ||||||
|  | 			if repo.Name == "." || repo.Name == ".." { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			user := new(User) | ||||||
|  | 			has, err := x.Where("id = ?", repo.OwnerID).Get(user) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err) | ||||||
|  | 			} else if !has { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git" | ||||||
|  | 			countObject, err := git.GetRepoSize(repoPath) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Warn("GetRepoSize: %v", err) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			repo.Size = countObject.Size + countObject.SizePack | ||||||
|  | 			if _, err = x.Id(repo.ID).Cols("size").Update(repo); err != nil { | ||||||
|  | 				return fmt.Errorf("update size: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @ -207,6 +207,7 @@ type Repository struct { | |||||||
| 	IsFork   bool        `xorm:"INDEX NOT NULL DEFAULT false"` | 	IsFork   bool        `xorm:"INDEX NOT NULL DEFAULT false"` | ||||||
| 	ForkID   int64       `xorm:"INDEX"` | 	ForkID   int64       `xorm:"INDEX"` | ||||||
| 	BaseRepo *Repository `xorm:"-"` | 	BaseRepo *Repository `xorm:"-"` | ||||||
|  | 	Size     int64       `xorm:"NOT NULL DEFAULT 0"` | ||||||
| 
 | 
 | ||||||
| 	Created     time.Time `xorm:"-"` | 	Created     time.Time `xorm:"-"` | ||||||
| 	CreatedUnix int64     `xorm:"INDEX"` | 	CreatedUnix int64     `xorm:"INDEX"` | ||||||
| @ -546,6 +547,18 @@ func (repo *Repository) IsOwnedBy(userID int64) bool { | |||||||
| 	return repo.OwnerID == userID | 	return repo.OwnerID == userID | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // UpdateSize updates the repository size, calculating it using git.GetRepoSize
 | ||||||
|  | func (repo *Repository) UpdateSize() error { | ||||||
|  | 	repoInfoSize, err := git.GetRepoSize(repo.RepoPath()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("UpdateSize: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	repo.Size = repoInfoSize.Size + repoInfoSize.SizePack | ||||||
|  | 	_, err = x.ID(repo.ID).Cols("size").Update(repo) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // CanBeForked returns true if repository meets the requirements of being forked.
 | // CanBeForked returns true if repository meets the requirements of being forked.
 | ||||||
| func (repo *Repository) CanBeForked() bool { | func (repo *Repository) CanBeForked() bool { | ||||||
| 	return !repo.IsBare | 	return !repo.IsBare | ||||||
| @ -810,6 +823,10 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err = repo.UpdateSize(); err != nil { | ||||||
|  | 		log.Error(4, "Failed to update size for repository: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if opts.IsMirror { | 	if opts.IsMirror { | ||||||
| 		if _, err = x.InsertOne(&Mirror{ | 		if _, err = x.InsertOne(&Mirror{ | ||||||
| 			RepoID:      repo.ID, | 			RepoID:      repo.ID, | ||||||
| @ -1464,6 +1481,10 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e | |||||||
| 				return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err) | 				return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if err = repo.UpdateSize(); err != nil { | ||||||
|  | 			log.Error(4, "Failed to update size for repository: %v", err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| @ -2171,6 +2192,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err = repo.UpdateSize(); err != nil { | ||||||
|  | 		log.Error(4, "Failed to update size for repository: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Copy LFS meta objects in new session
 | 	// Copy LFS meta objects in new session
 | ||||||
| 	sess2 := x.NewSession() | 	sess2 := x.NewSession() | ||||||
| 	defer sessionRelease(sess2) | 	defer sessionRelease(sess2) | ||||||
|  | |||||||
| @ -147,6 +147,11 @@ func (m *Mirror) runSync() bool { | |||||||
| 		} | 		} | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := m.Repo.UpdateSize(); err != nil { | ||||||
|  | 		log.Error(4, "Failed to update size for mirror repository: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if m.Repo.HasWiki() { | 	if m.Repo.HasWiki() { | ||||||
| 		if _, stderr, err := process.GetManager().ExecDir( | 		if _, stderr, err := process.GetManager().ExecDir( | ||||||
| 			timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), | 			timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), | ||||||
|  | |||||||
| @ -101,6 +101,10 @@ func PushUpdate(opts PushUpdateOptions) (err error) { | |||||||
| 		return fmt.Errorf("GetRepositoryByName: %v", err) | 		return fmt.Errorf("GetRepositoryByName: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err = repo.UpdateSize(); err != nil { | ||||||
|  | 		log.Error(4, "Failed to update size for repository: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Push tags.
 | 	// Push tags.
 | ||||||
| 	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | 	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) { | ||||||
| 		if err := CommitRepoAction(CommitRepoActionOptions{ | 		if err := CommitRepoAction(CommitRepoActionOptions{ | ||||||
|  | |||||||
| @ -82,6 +82,9 @@ func NewFuncMap() []template.FuncMap { | |||||||
| 		"DateFmtShort": func(t time.Time) string { | 		"DateFmtShort": func(t time.Time) string { | ||||||
| 			return t.Format("Jan 02, 2006") | 			return t.Format("Jan 02, 2006") | ||||||
| 		}, | 		}, | ||||||
|  | 		"SizeFmt": func(s int64) string { | ||||||
|  | 			return base.FileSize(s) | ||||||
|  | 		}, | ||||||
| 		"List": List, | 		"List": List, | ||||||
| 		"SubStr": func(str string, start, length int) string { | 		"SubStr": func(str string, start, length int) string { | ||||||
| 			if len(str) == 0 { | 			if len(str) == 0 { | ||||||
|  | |||||||
| @ -1119,6 +1119,7 @@ repos.private = Private | |||||||
| repos.watches = Watches | repos.watches = Watches | ||||||
| repos.stars = Stars | repos.stars = Stars | ||||||
| repos.issues = Issues | repos.issues = Issues | ||||||
|  | repos.size = Size | ||||||
| 
 | 
 | ||||||
| auths.auth_manage_panel = Authentication Manage Panel | auths.auth_manage_panel = Authentication Manage Panel | ||||||
| auths.new = Add New Source | auths.new = Add New Source | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ | |||||||
| 						<th>{{.i18n.Tr "admin.repos.watches"}}</th> | 						<th>{{.i18n.Tr "admin.repos.watches"}}</th> | ||||||
| 						<th>{{.i18n.Tr "admin.repos.stars"}}</th> | 						<th>{{.i18n.Tr "admin.repos.stars"}}</th> | ||||||
| 						<th>{{.i18n.Tr "admin.repos.issues"}}</th> | 						<th>{{.i18n.Tr "admin.repos.issues"}}</th> | ||||||
|  | 						<th>{{.i18n.Tr "admin.repos.size"}}</th> | ||||||
| 						<th>{{.i18n.Tr "admin.users.created"}}</th> | 						<th>{{.i18n.Tr "admin.users.created"}}</th> | ||||||
| 						<th>{{.i18n.Tr "admin.notices.op"}}</th> | 						<th>{{.i18n.Tr "admin.notices.op"}}</th> | ||||||
| 					</tr> | 					</tr> | ||||||
| @ -34,6 +35,7 @@ | |||||||
| 							<td>{{.NumWatches}}</td> | 							<td>{{.NumWatches}}</td> | ||||||
| 							<td>{{.NumStars}}</td> | 							<td>{{.NumStars}}</td> | ||||||
| 							<td>{{.NumIssues}}</td> | 							<td>{{.NumIssues}}</td> | ||||||
|  | 							<td>{{SizeFmt .Size}}</td> | ||||||
| 							<td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td> | 							<td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td> | ||||||
| 							<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Current}}" data-id="{{.ID}}"><i class="trash icon text red"></i></a></td> | 							<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Current}}" data-id="{{.ID}}"><i class="trash icon text red"></i></a></td> | ||||||
| 						</tr> | 						</tr> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user