File view with blame information shown in the left gutter beside each line.

barerepo / server / internal/hook/blobs.go
120 lines · 3.5kb · 133728eaa05486504991b230f9d4c7e986b7defc
log files threads runs releases config jump to file t
133728e barerepo 1mo
1
package hook
133728e barerepo 1mo
2
133728e barerepo 1mo
3
import (
133728e barerepo 1mo
4
"context"
133728e barerepo 1mo
5
"fmt"
133728e barerepo 1mo
6
"sort"
133728e barerepo 1mo
7
"strconv"
133728e barerepo 1mo
8
"strings"
133728e barerepo 1mo
9
133728e barerepo 1mo
10
"github.com/barerepo/server/internal/gitx"
133728e barerepo 1mo
11
)
133728e barerepo 1mo
12
133728e barerepo 1mo
13
// blob is one object a push adds, with the path it takes in the tree.
133728e barerepo 1mo
14
type blob struct {
133728e barerepo 1mo
15
Path string
133728e barerepo 1mo
16
Size int64
133728e barerepo 1mo
17
}
133728e barerepo 1mo
18
133728e barerepo 1mo
19
// pushed is what one update adds, which both limits in chapter 20.2 are measured against.
133728e barerepo 1mo
20
type pushed struct {
133728e barerepo 1mo
21
Over []blob
133728e barerepo 1mo
22
Bytes int64
133728e barerepo 1mo
23
}
133728e barerepo 1mo
24
133728e barerepo 1mo
25
// inspect reads the pushed range once since both limits ask the same objects, and counted holds what earlier refs added, because 20.2's push limit is one number for the whole push.
133728e barerepo 1mo
26
func inspect(ctx context.Context, dir string, u Update, blobLimitMB int, counted map[string]bool) pushed {
133728e barerepo 1mo
27
if u.Deleting() || !gitx.ValidRev(u.New) {
133728e barerepo 1mo
28
return pushed{}
133728e barerepo 1mo
29
}
133728e barerepo 1mo
30
limit := int64(blobLimitMB) << 20
133728e barerepo 1mo
31
133728e barerepo 1mo
32
// The pushed range, not the whole repository: --all still holds the old tips inside a hook.
133728e barerepo 1mo
33
listed, err := gitx.Run(ctx, dir, "rev-list", "--objects", u.New, "--not", "--all")
133728e barerepo 1mo
34
if err != nil {
133728e barerepo 1mo
35
return pushed{}
133728e barerepo 1mo
36
}
133728e barerepo 1mo
37
path := map[string]string{}
133728e barerepo 1mo
38
ids := make([]string, 0, 64)
133728e barerepo 1mo
39
for _, line := range strings.Split(strings.TrimRight(listed, "\n"), "\n") {
133728e barerepo 1mo
40
id, name, ok := strings.Cut(line, " ")
133728e barerepo 1mo
41
if !ok || len(id) < 40 {
133728e barerepo 1mo
42
continue
133728e barerepo 1mo
43
}
133728e barerepo 1mo
44
if counted[id] {
133728e barerepo 1mo
45
continue
133728e barerepo 1mo
46
}
133728e barerepo 1mo
47
counted[id] = true
133728e barerepo 1mo
48
path[id] = name
133728e barerepo 1mo
49
ids = append(ids, id)
133728e barerepo 1mo
50
}
133728e barerepo 1mo
51
if len(ids) == 0 {
133728e barerepo 1mo
52
return pushed{}
133728e barerepo 1mo
53
}
133728e barerepo 1mo
54
133728e barerepo 1mo
55
checked, err := gitx.RunStdin(ctx, dir, strings.Join(ids, "\n")+"\n", "cat-file", "--batch-check")
133728e barerepo 1mo
56
if err != nil {
133728e barerepo 1mo
57
return pushed{}
133728e barerepo 1mo
58
}
133728e barerepo 1mo
59
var out pushed
133728e barerepo 1mo
60
for _, line := range strings.Split(strings.TrimRight(checked, "\n"), "\n") {
133728e barerepo 1mo
61
f := strings.Fields(line)
133728e barerepo 1mo
62
if len(f) != 3 {
133728e barerepo 1mo
63
continue
133728e barerepo 1mo
64
}
133728e barerepo 1mo
65
n, err := strconv.ParseInt(f[2], 10, 64)
133728e barerepo 1mo
66
if err != nil {
133728e barerepo 1mo
67
continue
133728e barerepo 1mo
68
}
133728e barerepo 1mo
69
out.Bytes += n
133728e barerepo 1mo
70
if f[1] == "blob" && blobLimitMB > 0 && n > limit {
133728e barerepo 1mo
71
out.Over = append(out.Over, blob{Path: path[f[0]], Size: n})
133728e barerepo 1mo
72
}
133728e barerepo 1mo
73
}
133728e barerepo 1mo
74
// The path settles a tie, or two files of one size take turns being named, and chapter 20.2 wants the rejection to name the file.
133728e barerepo 1mo
75
sort.Slice(out.Over, func(i, j int) bool {
133728e barerepo 1mo
76
if out.Over[i].Size == out.Over[j].Size {
133728e barerepo 1mo
77
return out.Over[i].Path < out.Over[j].Path
133728e barerepo 1mo
78
}
133728e barerepo 1mo
79
return out.Over[i].Size > out.Over[j].Size
133728e barerepo 1mo
80
})
133728e barerepo 1mo
81
return out
133728e barerepo 1mo
82
}
133728e barerepo 1mo
83
133728e barerepo 1mo
84
// tooMuch is the whole push, which config calls loose because a first import is what meets it.
133728e barerepo 1mo
85
func tooMuch(bytes int64, limitMB int) string {
133728e barerepo 1mo
86
return fmt.Sprintf("this push adds %s of objects. the limit is %dmb.\n"+
133728e barerepo 1mo
87
"push it in parts, or ask whoever runs this barerepo to raise [limits] max_push_mb.",
133728e barerepo 1mo
88
blobSize(bytes), limitMB)
133728e barerepo 1mo
89
}
133728e barerepo 1mo
90
133728e barerepo 1mo
91
// tooBig is chapter 20.2's message, which names the file, because "push too large" sends a user hunting.
133728e barerepo 1mo
92
func tooBig(over []blob, limitMB int) string {
133728e barerepo 1mo
93
var b strings.Builder
133728e barerepo 1mo
94
fmt.Fprintf(&b, "%s is %s. the limit is %dmb.\n", over[0].Path, blobSize(over[0].Size), limitMB)
133728e barerepo 1mo
95
if len(over) > 1 {
133728e barerepo 1mo
96
fmt.Fprintf(&b, "%d more files in this push are over it too.\n", len(over)-1)
133728e barerepo 1mo
97
}
133728e barerepo 1mo
98
b.WriteString("\nlarge files belong in object storage, with a url or a checksum in the\n")
133728e barerepo 1mo
99
b.WriteString("repository. the build fetches them.")
133728e barerepo 1mo
100
return b.String()
133728e barerepo 1mo
101
}
133728e barerepo 1mo
102
133728e barerepo 1mo
103
func blobSize(n int64) string {
133728e barerepo 1mo
104
switch {
133728e barerepo 1mo
105
case n < 1024:
133728e barerepo 1mo
106
return fmt.Sprintf("%db", n)
133728e barerepo 1mo
107
case n < 1024*1024:
133728e barerepo 1mo
108
return blobUnit(float64(n)/1024, "kb")
133728e barerepo 1mo
109
default:
133728e barerepo 1mo
110
return blobUnit(float64(n)/(1024*1024), "mb")
133728e barerepo 1mo
111
}
133728e barerepo 1mo
112
}
133728e barerepo 1mo
113
133728e barerepo 1mo
114
// blobUnit writes a size the way the pages do, so 88kb is not 88.0kb here, and it is written twice because the hook binary cannot reach httpd.
133728e barerepo 1mo
115
func blobUnit(v float64, suffix string) string {
133728e barerepo 1mo
116
if v < 10 {
133728e barerepo 1mo
117
return fmt.Sprintf("%.1f%s", v, suffix)
133728e barerepo 1mo
118
}
133728e barerepo 1mo
119
return fmt.Sprintf("%.0f%s", v, suffix)
133728e barerepo 1mo
120
}
history · rawbarerepo 0.1.0