[mirror] Scalable static site server for Git forges (like GitHub Pages)
1package git_pages
2
3import (
4 "bytes"
5 "cmp"
6 "context"
7 "slices"
8 "strings"
9
10 "github.com/go-git/go-git/v6/plumbing/format/gitattributes"
11)
12
13func ReadGitAttributes(ctx context.Context, manifest *Manifest) gitattributes.Matcher {
14 type entryPair struct {
15 parts []string
16 entry *Entry
17 }
18
19 // Collect all .gitattributes files.
20 var files []entryPair
21 for name, entry := range manifest.GetContents() {
22 switch entry.GetType() {
23 case Type_InlineFile, Type_ExternalFile:
24 parts := strings.Split(name, "/")
25 if parts[len(parts)-1] == ".gitattributes" {
26 files = append(files, entryPair{parts, entry})
27 }
28 }
29 }
30
31 // Sort the file list by depth, then by name.
32 slices.SortFunc(files, func(a entryPair, b entryPair) int {
33 return cmp.Or(
34 cmp.Compare(len(a.parts), len(b.parts)),
35 slices.Compare(a.parts, b.parts),
36 )
37 })
38
39 // Gather all .gitattributes rules, sorted by depth.
40 var rules []gitattributes.MatchAttribute
41 for _, pair := range files {
42 parts, entry := pair.parts, pair.entry
43 data, err := GetEntryContents(ctx, entry)
44 if err != nil {
45 continue
46 }
47 dirs := parts[:len(parts)-1]
48 isRoot := len(parts) == 1
49 newRules, err := gitattributes.ReadAttributes(bytes.NewReader(data), dirs, isRoot)
50 if err != nil {
51 AddProblem(manifest, strings.Join(parts, "/"), "parsing .gitattributes: %v", err)
52 continue
53 }
54 rules = append(rules, newRules...)
55 }
56
57 // gitattributes.Matcher applies rules in reverse.
58 slices.Reverse(rules)
59 matcher := gitattributes.NewMatcher(rules)
60 return matcher
61}