5580b5b65433239ee9a379172f26c30b30e2ff61
- Author
- Ayman Bagabas <ayman.bagabas@gmail.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/go.mod b/go.mod
2index 9c85d1d8f9683ea35402c02c75e17c9363ba30b5..e7ca5b691bd1264b1114d256ca39ee868b16f46f 100644
3--- a/go.mod
4+++ b/go.mod
5@@ -1,6 +1,6 @@
6 module github.com/charmbracelet/soft-serve
7
8-go 1.17
9+go 1.18
10
11 require (
12 github.com/alecthomas/chroma v0.10.0
13@@ -24,6 +24,7 @@ require (
14 github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103
15 github.com/gobwas/glob v0.2.3
16 github.com/gogs/git-module v1.8.2
17+ github.com/hashicorp/golang-lru/v2 v2.0.2
18 github.com/jmoiron/sqlx v1.3.5
19 github.com/lrstanley/bubblezone v0.0.0-20220716194435-3cb8c52f6a8f
20 github.com/muesli/mango-cobra v1.2.0
21diff --git a/go.sum b/go.sum
22index c35eafe2b45fb008585ae5060f9b9ac0823d2c89..3cbdc98fb3c8fb6879af328bfd44a958c92dd536 100644
23--- a/go.sum
24+++ b/go.sum
25@@ -1,67 +1,18 @@
26-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
27-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
28-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
29-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
30-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
31-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
32-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
33-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
34-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
35-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
36-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
37-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
38-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
39-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
40-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
41-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
42-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
43-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
44-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
45-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
46-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
47-cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
48-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
49-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
50-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
51-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
52-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
53-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
54-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
55-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
56-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
57-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
58-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
59-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
60-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
61-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
62 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
63 github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
64 github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
65 github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
66 github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
67-github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
68-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
69-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
70-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
71-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
72-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
73-github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
74 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
75 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
76 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
77 github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
78 github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
79-github.com/aymanbagabas/git-module v1.4.1-0.20230509180555-975c24cdb79a h1:rY724fIR0NNR/UTXJufwKwYz+sNYVve/ZdzWX39xMqM=
80-github.com/aymanbagabas/git-module v1.4.1-0.20230509180555-975c24cdb79a/go.mod h1:GUSSUH+RM7fZOtjhS6Obh4B9aAvs3EeROpazfMNMF8g=
81 github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
82-github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E=
83-github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
84 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
85 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
86 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
87 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
88-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
89-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
90 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
91 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
92 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
93@@ -69,16 +20,11 @@ github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg
94 github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
95 github.com/caarlos0/sshmarshal v0.1.0 h1:zTCZrDORFfWh526Tsb7vCm3+Yg/SfW/Ub8aQDeosk0I=
96 github.com/caarlos0/sshmarshal v0.1.0/go.mod h1:7Pd/0mmq9x/JCzKauogNjSQEhivBclCQHfr9dlpDIyA=
97-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
98-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
99-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
100 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
101 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
102 github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI=
103 github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74=
104-github.com/charmbracelet/bubbletea v0.22.0/go.mod h1:aoVIwlNlr5wbCB26KhxfrqAn0bMp4YpJcoOelbxApjs=
105 github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
106-github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM=
107 github.com/charmbracelet/bubbletea v0.24.0 h1:l8PHrft/GIeikDPCUhQe53AJrDD8xGSn0Agirh8xbe8=
108 github.com/charmbracelet/bubbletea v0.24.0/go.mod h1:rK3g/2+T8vOSEkNHvtq40umJpeVYDn6bLaqbgzhL/hg=
109 github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
110@@ -86,7 +32,6 @@ github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8o
111 github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
112 github.com/charmbracelet/keygen v0.4.2 h1:TNHua2MlXc6W1dQB2iW4msSZGKlb8RtxtmYDWUs4iRw=
113 github.com/charmbracelet/keygen v0.4.2/go.mod h1:4e4FT3HSdLU/u83RfJWvzJIaVb8aX4MxtDlfXwpDJaI=
114-github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
115 github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
116 github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
117 github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
118@@ -96,15 +41,7 @@ github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 h1:wpHMERIN0pQZE
119 github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103/go.mod h1:0Vm2/8yBljiLDnGJHU8ehswfawrEybGk33j5ssqKQVM=
120 github.com/charmbracelet/wish v1.1.1 h1:KdICASKd2oh2JPvk1Z4CJtAi97cFErXF7NKienPICO4=
121 github.com/charmbracelet/wish v1.1.1/go.mod h1:xh4KZpSULw+Xqb9bcbhw92QAinVB75CVLWrFuyY6IVs=
122-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
123-github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
124-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
125diff --git a/server/backend/sqlite/hooks.go b/server/backend/sqlite/hooks.go
126index 0f9d61658d0b061bc37c14e807c614444f5cc6c7..b3691b187e973f43ca0aeb90ba24b274da7d586a 100644
127--- a/server/backend/sqlite/hooks.go
128+++ b/server/backend/sqlite/hooks.go
129@@ -40,24 +40,58 @@ func (d *SqliteBackend) PostUpdate(stdout io.Writer, stderr io.Writer, repo stri
130 wg.Add(1)
131 go func() {
132 defer wg.Done()
133-
134- rr, err := d.Repository(repo)
135- if err != nil {
136- d.logger.Error("error getting repository", "repo", repo, "err", err)
137- return
138- }
139-
140- r, err := rr.Open()
141- if err != nil {
142- d.logger.Error("error opening repository", "repo", repo, "err", err)
143+ if err := updateServerInfo(d, repo); err != nil {
144+ d.logger.Error("error updating server-info", "repo", repo, "err", err)
145 return
146 }
147+ }()
148
149- if err := r.UpdateServerInfo(); err != nil {
150- d.logger.Error("error updating server-info", "repo", repo, "err", err)
151+ // Populate last-modified file.
152+ wg.Add(1)
153+ go func() {
154+ defer wg.Done()
155+ if err := populateLastModified(d, repo); err != nil {
156+ d.logger.Error("error populating last-modified", "repo", repo, "err", err)
157 return
158 }
159 }()
160
161 wg.Wait()
162 }
163+
164+func updateServerInfo(d *SqliteBackend, repo string) error {
165+ rr, err := d.Repository(repo)
166+ if err != nil {
167+ return err
168+ }
169+
170+ r, err := rr.Open()
171+ if err != nil {
172+ return err
173+ }
174+
175+ return r.UpdateServerInfo()
176+}
177+
178+func populateLastModified(d *SqliteBackend, repo string) error {
179+ var rr *Repo
180+ if rr, err := d.Repository(repo); err != nil {
181+ return err
182+ } else if r, ok := rr.(*Repo); ok {
183+ rr = r
184+ } else {
185+ return ErrRepoNotExist
186+ }
187+
188+ r, err := rr.Open()
189+ if err != nil {
190+ return err
191+ }
192+
193+ c, err := r.LatestCommitTime()
194+ if err != nil {
195+ return err
196+ }
197+
198+ return rr.writeLastModified(c)
199+}
200diff --git a/server/backend/sqlite/repo.go b/server/backend/sqlite/repo.go
201index 6b90ca92cc805186a1e7af8fa769283120358a47..34b5e6baa597d7271f61e30189c006105ce8aee0 100644
202--- a/server/backend/sqlite/repo.go
203+++ b/server/backend/sqlite/repo.go
204@@ -1,7 +1,11 @@
205 package sqlite
206
207 import (
208+ "bufio"
209 "context"
210+ "os"
211+ "path/filepath"
212+ "sync"
213 "time"
214
215 "github.com/charmbracelet/soft-serve/git"
216@@ -16,12 +20,27 @@ type Repo struct {
217 name string
218 path string
219 db *sqlx.DB
220+
221+ // cache
222+ // updatedAt is cached in "last-modified" file.
223+ mu sync.Mutex
224+ desc *string
225+ projectName *string
226+ isMirror *bool
227+ isPrivate *bool
228+ isHidden *bool
229 }
230
231 // Description returns the repository's description.
232 //
233 // It implements backend.Repository.
234 func (r *Repo) Description() string {
235+ r.mu.Lock()
236+ defer r.mu.Unlock()
237+ if r.desc != nil {
238+ return *r.desc
239+ }
240+
241 var desc string
242 if err := wrapTx(r.db, context.Background(), func(tx *sqlx.Tx) error {
243 return tx.Get(&desc, "SELECT description FROM repo WHERE name = ?", r.name)
244@@ -29,6 +48,7 @@ func (r *Repo) Description() string {
245 return ""
246 }
247
248+ r.desc = &desc
249 return desc
250 }
251
252@@ -36,6 +56,12 @@ func (r *Repo) Description() string {
253 //
254 // It implements backend.Repository.
255 func (r *Repo) IsMirror() bool {
256+ r.mu.Lock()
257+ defer r.mu.Unlock()
258+ if r.isMirror != nil {
259+ return *r.isMirror
260+ }
261+
262 var mirror bool
263 if err := wrapTx(r.db, context.Background(), func(tx *sqlx.Tx) error {
264 return tx.Get(&mirror, "SELECT mirror FROM repo WHERE name = ?", r.name)
265@@ -43,6 +69,7 @@ func (r *Repo) IsMirror() bool {
266 return false
267 }
268
269+ r.isMirror = &mirror
270 return mirror
271 }
272
273@@ -50,6 +77,12 @@ func (r *Repo) IsMirror() bool {
274 //
275 // It implements backend.Repository.
276 func (r *Repo) IsPrivate() bool {
277+ r.mu.Lock()
278+ defer r.mu.Unlock()
279+ if r.isPrivate != nil {
280+ return *r.isPrivate
281+ }
282+
283 var private bool
284 if err := wrapTx(r.db, context.Background(), func(tx *sqlx.Tx) error {
285 return tx.Get(&private, "SELECT private FROM repo WHERE name = ?", r.name)
286@@ -57,6 +90,7 @@ func (r *Repo) IsPrivate() bool {
287 return false
288 }
289
290+ r.isPrivate = &private
291 return private
292 }
293
294@@ -78,6 +112,12 @@ func (r *Repo) Open() (*git.Repository, error) {
295 //
296 // It implements backend.Repository.
297 func (r *Repo) ProjectName() string {
298+ r.mu.Lock()
299+ defer r.mu.Unlock()
300+ if r.projectName != nil {
301+ return *r.projectName
302+ }
303+
304diff --git a/server/backend/sqlite/sqlite.go b/server/backend/sqlite/sqlite.go
305index a96523c33f299b8b62878f8b1f54e6fac63001b9..e4c9111325d8c0c317325a4ffa39510c932290f3 100644
306--- a/server/backend/sqlite/sqlite.go
307+++ b/server/backend/sqlite/sqlite.go
308@@ -14,6 +14,7 @@ import (
309 "github.com/charmbracelet/soft-serve/server/config"
310 "github.com/charmbracelet/soft-serve/server/hooks"
311 "github.com/charmbracelet/soft-serve/server/utils"
312+ lru "github.com/hashicorp/golang-lru/v2"
313 "github.com/jmoiron/sqlx"
314 _ "modernc.org/sqlite" // sqlite driver
315 )
316@@ -26,6 +27,9 @@ type SqliteBackend struct { //nolint: revive
317 dp string
318 db *sqlx.DB
319 logger *log.Logger
320+
321+ // Repositories cache
322+ cache *cache
323 }
324
325 var _ backend.Backend = (*SqliteBackend)(nil)
326@@ -56,6 +60,9 @@ func NewSqliteBackend(ctx context.Context) (*SqliteBackend, error) {
327 logger: log.FromContext(ctx).WithPrefix("sqlite"),
328 }
329
330+ // Set up LRU cache with size 1000
331+ d.cache = newCache(d, 1000)
332+
333 if err := d.init(); err != nil {
334 return nil, err
335 }
336@@ -170,6 +177,9 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt
337 db: d.db,
338 }
339
340+ // Set cache
341+ d.cache.Set(name, r)
342+
343 return r, d.initRepo(name)
344 }
345
346@@ -188,9 +198,9 @@ func (d *SqliteBackend) ImportRepository(name string, remote string, opts backen
347 }
348
349 copts := git.CloneOptions{
350- Bare: true,
351- Mirror: opts.Mirror,
352- Quiet: true,
353+ Bare: true,
354+ Mirror: opts.Mirror,
355+ Quiet: true,
356 CommandOptions: git.CommandOptions{
357 Timeout: -1,
358 Context: d.ctx,
359@@ -201,6 +211,7 @@ func (d *SqliteBackend) ImportRepository(name string, remote string, opts backen
360 ),
361 },
362 },
363+ // Timeout: time.Hour,
364 }
365
366 if err := git.Clone(remote, rp, copts); err != nil {
367@@ -223,6 +234,9 @@ func (d *SqliteBackend) DeleteRepository(name string) error {
368 rp := filepath.Join(d.reposPath(), repo)
369
370 return wrapTx(d.db, d.ctx, func(tx *sqlx.Tx) error {
371+ // Delete repo from cache
372+ defer d.cache.Delete(name)
373+
374 if err := os.RemoveAll(rp); err != nil {
375 return err
376 }
377@@ -268,6 +282,12 @@ func (d *SqliteBackend) RenameRepository(oldName string, newName string) error {
378 return err
379 }
380
381+ // Delete cache
382+ d.cache.Delete(oldName)
383+ defer func() {
384+ d.Repository(newName) // nolint: errcheck
385+ }()
386+
387 return os.Rename(op, np)
388 }
389
390@@ -276,6 +296,7 @@ func (d *SqliteBackend) RenameRepository(oldName string, newName string) error {
391 // It implements backend.Backend.
392 func (d *SqliteBackend) Repositories() ([]backend.Repository, error) {
393 repos := make([]backend.Repository, 0)
394+
395 if err := wrapTx(d.db, d.ctx, func(tx *sqlx.Tx) error {
396 rows, err := tx.Query("SELECT name FROM repo")
397 if err != nil {
398@@ -289,11 +310,21 @@ func (d *SqliteBackend) Repositories() ([]backend.Repository, error) {
399 return err
400 }
401
402- repos = append(repos, &Repo{
403+ if r, ok := d.cache.Get(name); ok && r != nil {
404+ repos = append(repos, r)
405+ continue
406+ }
407+