Browse Source

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
Jonas 2 years ago
parent
commit
be6edaddcb

+ 2 - 0
models/migrations/migrations.go

@@ -104,6 +104,8 @@ var migrations = []Migration{
104 104
 	NewMigration("generate and migrate repo and wiki Git hooks", generateAndMigrateGitHookChains),
105 105
 	// v27 -> v28
106 106
 	NewMigration("change mirror interval from hours to time.Duration", convertIntervalToDuration),
107
+	// v28 -> v29
108
+	NewMigration("add field for repo size", addRepoSize),
107 109
 }
108 110
 
109 111
 // Migrate database to current version

+ 77 - 0
models/migrations/v28.go

@@ -0,0 +1,77 @@
1
+// Copyright 2017 The Gogs Authors. All rights reserved.
2
+// Copyright 2017 Gitea. All rights reserved.
3
+// Use of this source code is governed by a MIT-style
4
+// license that can be found in the LICENSE file.
5
+
6
+package migrations
7
+
8
+import (
9
+	"fmt"
10
+	"path/filepath"
11
+	"strings"
12
+
13
+	"code.gitea.io/git"
14
+	"code.gitea.io/gitea/modules/log"
15
+	"code.gitea.io/gitea/modules/setting"
16
+
17
+	"github.com/go-xorm/xorm"
18
+)
19
+
20
+func addRepoSize(x *xorm.Engine) (err error) {
21
+	log.Info("This migration could take up to minutes, please be patient.")
22
+	type Repository struct {
23
+		ID      int64
24
+		OwnerID int64
25
+		Name    string
26
+		Size    int64
27
+	}
28
+	type User struct {
29
+		ID   int64
30
+		Name string
31
+	}
32
+	if err = x.Sync2(new(Repository)); err != nil {
33
+		return fmt.Errorf("Sync2: %v", err)
34
+	}
35
+
36
+	// For the sake of SQLite3, we can't use x.Iterate here.
37
+	offset := 0
38
+	for {
39
+		repos := make([]*Repository, 0, 10)
40
+		if err = x.Sql(fmt.Sprintf("SELECT * FROM `repository` ORDER BY id ASC LIMIT 10 OFFSET %d", offset)).
41
+			Find(&repos); err != nil {
42
+			return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
43
+		}
44
+		log.Trace("Select [offset: %d, repos: %d]", offset, len(repos))
45
+		if len(repos) == 0 {
46
+			break
47
+		}
48
+		offset += 10
49
+
50
+		for _, repo := range repos {
51
+			if repo.Name == "." || repo.Name == ".." {
52
+				continue
53
+			}
54
+
55
+			user := new(User)
56
+			has, err := x.Where("id = ?", repo.OwnerID).Get(user)
57
+			if err != nil {
58
+				return fmt.Errorf("query owner of repository [repo_id: %d, owner_id: %d]: %v", repo.ID, repo.OwnerID, err)
59
+			} else if !has {
60
+				continue
61
+			}
62
+
63
+			repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(user.Name), strings.ToLower(repo.Name)) + ".git"
64
+			countObject, err := git.GetRepoSize(repoPath)
65
+			if err != nil {
66
+				log.Warn("GetRepoSize: %v", err)
67
+				continue
68
+			}
69
+
70
+			repo.Size = countObject.Size + countObject.SizePack
71
+			if _, err = x.Id(repo.ID).Cols("size").Update(repo); err != nil {
72
+				return fmt.Errorf("update size: %v", err)
73
+			}
74
+		}
75
+	}
76
+	return nil
77
+}

+ 25 - 0
models/repo.go

@@ -207,6 +207,7 @@ type Repository struct {
207 207
 	IsFork   bool        `xorm:"INDEX NOT NULL DEFAULT false"`
208 208
 	ForkID   int64       `xorm:"INDEX"`
209 209
 	BaseRepo *Repository `xorm:"-"`
210
+	Size     int64       `xorm:"NOT NULL DEFAULT 0"`
210 211
 
211 212
 	Created     time.Time `xorm:"-"`
212 213
 	CreatedUnix int64     `xorm:"INDEX"`
@@ -546,6 +547,18 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
546 547
 	return repo.OwnerID == userID
547 548
 }
548 549
 
550
+// UpdateSize updates the repository size, calculating it using git.GetRepoSize
551
+func (repo *Repository) UpdateSize() error {
552
+	repoInfoSize, err := git.GetRepoSize(repo.RepoPath())
553
+	if err != nil {
554
+		return fmt.Errorf("UpdateSize: %v", err)
555
+	}
556
+
557
+	repo.Size = repoInfoSize.Size + repoInfoSize.SizePack
558
+	_, err = x.ID(repo.ID).Cols("size").Update(repo)
559
+	return err
560
+}
561
+
549 562
 // CanBeForked returns true if repository meets the requirements of being forked.
550 563
 func (repo *Repository) CanBeForked() bool {
551 564
 	return !repo.IsBare
@@ -810,6 +823,10 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
810 823
 		}
811 824
 	}
812 825
 
826
+	if err = repo.UpdateSize(); err != nil {
827
+		log.Error(4, "Failed to update size for repository: %v", err)
828
+	}
829
+
813 830
 	if opts.IsMirror {
814 831
 		if _, err = x.InsertOne(&Mirror{
815 832
 			RepoID:      repo.ID,
@@ -1464,6 +1481,10 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
1464 1481
 				return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err)
1465 1482
 			}
1466 1483
 		}
1484
+
1485
+		if err = repo.UpdateSize(); err != nil {
1486
+			log.Error(4, "Failed to update size for repository: %v", err)
1487
+		}
1467 1488
 	}
1468 1489
 
1469 1490
 	return nil
@@ -2171,6 +2192,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
2171 2192
 		return nil, err
2172 2193
 	}
2173 2194
 
2195
+	if err = repo.UpdateSize(); err != nil {
2196
+		log.Error(4, "Failed to update size for repository: %v", err)
2197
+	}
2198
+
2174 2199
 	// Copy LFS meta objects in new session
2175 2200
 	sess2 := x.NewSession()
2176 2201
 	defer sessionRelease(sess2)

+ 5 - 0
models/repo_mirror.go

@@ -147,6 +147,11 @@ func (m *Mirror) runSync() bool {
147 147
 		}
148 148
 		return false
149 149
 	}
150
+
151
+	if err := m.Repo.UpdateSize(); err != nil {
152
+		log.Error(4, "Failed to update size for mirror repository: %v", err)
153
+	}
154
+
150 155
 	if m.Repo.HasWiki() {
151 156
 		if _, stderr, err := process.GetManager().ExecDir(
152 157
 			timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath),

+ 4 - 0
models/update.go

@@ -101,6 +101,10 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
101 101
 		return fmt.Errorf("GetRepositoryByName: %v", err)
102 102
 	}
103 103
 
104
+	if err = repo.UpdateSize(); err != nil {
105
+		log.Error(4, "Failed to update size for repository: %v", err)
106
+	}
107
+
104 108
 	// Push tags.
105 109
 	if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
106 110
 		if err := CommitRepoAction(CommitRepoActionOptions{

+ 3 - 0
modules/templates/helper.go

@@ -82,6 +82,9 @@ func NewFuncMap() []template.FuncMap {
82 82
 		"DateFmtShort": func(t time.Time) string {
83 83
 			return t.Format("Jan 02, 2006")
84 84
 		},
85
+		"SizeFmt": func(s int64) string {
86
+			return base.FileSize(s)
87
+		},
85 88
 		"List": List,
86 89
 		"SubStr": func(str string, start, length int) string {
87 90
 			if len(str) == 0 {

+ 1 - 0
options/locale/locale_en-US.ini

@@ -1119,6 +1119,7 @@ repos.private = Private
1119 1119
 repos.watches = Watches
1120 1120
 repos.stars = Stars
1121 1121
 repos.issues = Issues
1122
+repos.size = Size
1122 1123
 
1123 1124
 auths.auth_manage_panel = Authentication Manage Panel
1124 1125
 auths.new = Add New Source

+ 2 - 0
templates/admin/repo/list.tmpl

@@ -20,6 +20,7 @@
20 20
 						<th>{{.i18n.Tr "admin.repos.watches"}}</th>
21 21
 						<th>{{.i18n.Tr "admin.repos.stars"}}</th>
22 22
 						<th>{{.i18n.Tr "admin.repos.issues"}}</th>
23
+						<th>{{.i18n.Tr "admin.repos.size"}}</th>
23 24
 						<th>{{.i18n.Tr "admin.users.created"}}</th>
24 25
 						<th>{{.i18n.Tr "admin.notices.op"}}</th>
25 26
 					</tr>
@@ -34,6 +35,7 @@
34 35
 							<td>{{.NumWatches}}</td>
35 36
 							<td>{{.NumStars}}</td>
36 37
 							<td>{{.NumIssues}}</td>
38
+							<td>{{SizeFmt .Size}}</td>
37 39
 							<td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td>
38 40
 							<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>
39 41
 						</tr>