+1
-1
flake.nix
+1
-1
flake.nix
···
49
49
inherit (gitignore.lib) gitignoreSource;
50
50
in {
51
51
overlays.default = final: prev: let
52
-
goModHash = "sha256-SfyLSQa3g30PXCi/VQOm0cz372B6YdXE7xfBlhgCGec=";
52
+
goModHash = "sha256-EooM036KFlO4Zot5vDLX+HFU2GfjCtjVEfF7t+d4Avk=";
53
53
buildCmdPackage = name:
54
54
final.buildGoModule {
55
55
pname = name;
+3
-1
go.mod
+3
-1
go.mod
···
16
16
github.com/dustin/go-humanize v1.0.1
17
17
github.com/gliderlabs/ssh v0.3.8
18
18
github.com/go-chi/chi/v5 v5.2.0
19
-
github.com/go-git/go-git/v5 v5.0.0-00010101000000-000000000000
19
+
github.com/go-enry/go-enry/v2 v2.9.2
20
+
github.com/go-git/go-git/v5 v5.14.0
20
21
github.com/google/uuid v1.6.0
21
22
github.com/gorilla/sessions v1.4.0
22
23
github.com/haileyok/atproto-oauth-golang v0.0.2
···
49
50
github.com/dlclark/regexp2 v1.11.5 // indirect
50
51
github.com/emirpasic/gods v1.18.1 // indirect
51
52
github.com/felixge/httpsnoop v1.0.4 // indirect
53
+
github.com/go-enry/go-oniguruma v1.2.1 // indirect
52
54
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
53
55
github.com/go-git/go-billy/v5 v5.6.2 // indirect
54
56
github.com/go-logr/logr v1.4.2 // indirect
+5
go.sum
+5
go.sum
···
69
69
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
70
70
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
71
71
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
72
+
github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
73
+
github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
74
+
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
75
+
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
72
76
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
73
77
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
74
78
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
···
263
267
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
264
268
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
265
269
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
270
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
266
271
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
267
272
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
268
273
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+36
knotserver/git/git.go
+36
knotserver/git/git.go
···
169
169
return c, nil
170
170
}
171
171
172
+
func (g *GitRepo) FileContentN(path string, cap int64) ([]byte, error) {
173
+
buf := []byte{}
174
+
175
+
c, err := g.r.CommitObject(g.h)
176
+
if err != nil {
177
+
return nil, fmt.Errorf("commit object: %w", err)
178
+
}
179
+
180
+
tree, err := c.Tree()
181
+
if err != nil {
182
+
return nil, fmt.Errorf("file tree: %w", err)
183
+
}
184
+
185
+
file, err := tree.File(path)
186
+
if err != nil {
187
+
return nil, err
188
+
}
189
+
190
+
isbin, _ := file.IsBinary()
191
+
192
+
if !isbin {
193
+
reader, err := file.Reader()
194
+
if err != nil {
195
+
return nil, err
196
+
}
197
+
bufReader := io.LimitReader(reader, cap)
198
+
_, err = bufReader.Read(buf)
199
+
if err != nil {
200
+
return nil, err
201
+
}
202
+
return buf, nil
203
+
} else {
204
+
return nil, ErrBinaryFile
205
+
}
206
+
}
207
+
172
208
func (g *GitRepo) FileContent(path string) (string, error) {
173
209
c, err := g.r.CommitObject(g.h)
174
210
if err != nil {
+1
knotserver/handler.go
+1
knotserver/handler.go
+102
knotserver/routes.go
+102
knotserver/routes.go
···
12
12
"net/http"
13
13
"net/url"
14
14
"os"
15
+
"path"
15
16
"path/filepath"
16
17
"strconv"
17
18
"strings"
···
19
20
securejoin "github.com/cyphar/filepath-securejoin"
20
21
"github.com/gliderlabs/ssh"
21
22
"github.com/go-chi/chi/v5"
23
+
"github.com/go-enry/go-enry/v2"
22
24
gogit "github.com/go-git/go-git/v5"
23
25
"github.com/go-git/go-git/v5/plumbing"
24
26
"github.com/go-git/go-git/v5/plumbing/object"
···
710
712
711
713
w.Header().Set("Content-Type", "application/json")
712
714
json.NewEncoder(w).Encode(types.AncestorCheckResponse{Status: status})
715
+
}
716
+
717
+
func (h *Handle) RepoLanguages(w http.ResponseWriter, r *http.Request) {
718
+
l := h.l.With("handler", "RepoForkSync")
719
+
720
+
data := struct {
721
+
Did string `json:"did"`
722
+
Source string `json:"source"`
723
+
Name string `json:"name,omitempty"`
724
+
}{}
725
+
726
+
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
727
+
writeError(w, "invalid request body", http.StatusBadRequest)
728
+
return
729
+
}
730
+
731
+
did := data.Did
732
+
source := data.Source
733
+
734
+
if did == "" || source == "" {
735
+
l.Error("invalid request body, empty did or name")
736
+
w.WriteHeader(http.StatusBadRequest)
737
+
return
738
+
}
739
+
740
+
var name string
741
+
if data.Name != "" {
742
+
name = data.Name
743
+
} else {
744
+
name = filepath.Base(source)
745
+
}
746
+
747
+
branch := chi.URLParam(r, "branch")
748
+
branch, _ = url.PathUnescape(branch)
749
+
750
+
relativeRepoPath := filepath.Join(did, name)
751
+
repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath)
752
+
753
+
gr, err := git.Open(repoPath, branch)
754
+
if err != nil {
755
+
log.Println(err)
756
+
notFound(w)
757
+
return
758
+
}
759
+
760
+
languageFileCount := make(map[string]int)
761
+
languagePercentage := make(map[string]float64)
762
+
763
+
err = recurseEntireTree(gr, func(absPath string) {
764
+
lang, safe := enry.GetLanguageByExtension(absPath)
765
+
if len(lang) == 0 || !safe {
766
+
content, _ := gr.FileContentN(absPath, 1024)
767
+
if !safe {
768
+
lang = enry.GetLanguage(absPath, content)
769
+
} else {
770
+
lang, _ = enry.GetLanguageByContent(absPath, content)
771
+
if len(lang) == 0 {
772
+
return
773
+
}
774
+
}
775
+
}
776
+
777
+
v, ok := languageFileCount[lang]
778
+
if ok {
779
+
languageFileCount[lang] = v + 1
780
+
} else {
781
+
languageFileCount[lang] = 1
782
+
}
783
+
}, "")
784
+
if err != nil {
785
+
log.Println(err)
786
+
writeError(w, err.Error(), http.StatusNoContent)
787
+
return
788
+
}
789
+
790
+
for path, fileCount := range languageFileCount {
791
+
percentage := float64(fileCount) / float64(len(languageFileCount)) * 100.0
792
+
languagePercentage[path] = percentage
793
+
}
794
+
795
+
w.Header().Set("Content-Type", "application/json")
796
+
json.NewEncoder(w).Encode(types.RepoLanguageResponse{Languages: languagePercentage})
797
+
}
798
+
799
+
func recurseEntireTree(git *git.GitRepo, callback func(absPath string), filePath string) error {
800
+
files, err := git.FileTree(filePath)
801
+
if err != nil {
802
+
log.Println(err)
803
+
return err
804
+
}
805
+
806
+
for _, file := range files {
807
+
absPath := path.Join(filePath, file.Name)
808
+
if !file.IsFile {
809
+
return recurseEntireTree(git, callback, absPath)
810
+
}
811
+
callback(absPath)
812
+
}
813
+
814
+
return nil
713
815
}
714
816
715
817
func (h *Handle) RepoForkSync(w http.ResponseWriter, r *http.Request) {
+5
types/repo.go
+5
types/repo.go