1package service
2
3import (
4 "compress/gzip"
5 "errors"
6 "io"
7 "log/slog"
8
9 "git.gabrielgio.me/cerrado/pkg/config"
10 "git.gabrielgio.me/cerrado/pkg/git"
11 "github.com/go-git/go-git/v5/plumbing"
12 "github.com/go-git/go-git/v5/plumbing/object"
13)
14
15type (
16 Repository struct {
17 Name string
18 Description string
19 LastCommitDate string
20 Ref string
21 }
22
23 GitService struct {
24 configRepo configurationRepository
25 }
26
27 configurationRepository interface {
28 List() []*config.GitRepositoryConfiguration
29 GetByName(name string) *config.GitRepositoryConfiguration
30 }
31)
32
33var ErrRepositoryNotFound = errors.New("Repository not found")
34
35// TODO: make it configurable
36const timeFormat = "2006.01.02 15:04:05"
37
38func NewGitService(configRepo configurationRepository) *GitService {
39 return &GitService{
40 configRepo: configRepo,
41 }
42}
43
44func (g *GitService) ListRepositories() ([]*Repository, error) {
45 rs := g.configRepo.List()
46
47 repos := make([]*Repository, 0, len(rs))
48 for _, r := range rs {
49 repo, err := git.OpenRepository(r.Path)
50 if err != nil {
51 return nil, err
52 }
53
54 obj, err := repo.LastCommit()
55 if err != nil {
56 slog.Error("Error fetching last commit", "repository", r.Path, "error", err)
57 continue
58 }
59
60 head, err := repo.Head()
61 if err != nil {
62 slog.Error("Error fetching head", "repository", r.Path, "error", err)
63 continue
64 }
65
66 repos = append(repos, &Repository{
67 Name: r.Name,
68 Description: r.Description,
69 LastCommitDate: obj.Author.When.Format(timeFormat),
70 Ref: head.Name().Short(),
71 })
72 }
73
74 return repos, nil
75}
76
77func (g *GitService) ListCommits(name, ref string, count int) ([]*object.Commit, error) {
78 r := g.configRepo.GetByName(name)
79 if r == nil {
80 return nil, ErrRepositoryNotFound
81 }
82
83 repo, err := git.OpenRepository(r.Path)
84 if err != nil {
85 return nil, err
86 }
87
88 err = repo.SetRef(ref)
89 if err != nil {
90 return nil, err
91 }
92 return repo.Commits(count)
93}
94
95func (g *GitService) LastCommit(name, ref string) (*object.Commit, error) {
96 r := g.configRepo.GetByName(name)
97 if r == nil {
98 return nil, ErrRepositoryNotFound
99 }
100
101 repo, err := git.OpenRepository(r.Path)
102 if err != nil {
103 return nil, err
104 }
105
106 err = repo.SetRef(ref)
107 if err != nil {
108 return nil, err
109 }
110
111 return repo.LastCommit()
112}
113
114func (g *GitService) WriteTarGZip(w io.Writer, name, ref string, prefix string) error {
115 r := g.configRepo.GetByName(name)
116 if r == nil {
117 return ErrRepositoryNotFound
118 }
119
120 repo, err := git.OpenRepository(r.Path)
121 if err != nil {
122 return err
123 }
124
125 err = repo.SetRef(ref)
126 if err != nil {
127 return err
128 }
129
130 gw := gzip.NewWriter(w)
131 defer gw.Close()
132
133 err = repo.WriteTar(gw, prefix)
134 if err != nil {
135 return err
136 }
137
138 return nil
139}
140
141func (g *GitService) Diff(name, ref string) (string, error) {
142 r := g.configRepo.GetByName(name)
143 if r == nil {
144 return "", ErrRepositoryNotFound
145 }
146
147 repo, err := git.OpenRepository(r.Path)
148 if err != nil {
149 return "", err
150 }
151
152 err = repo.SetRef(ref)
153 if err != nil {
154 return "", err
155 }
156
157 return repo.Diff()
158}
159
160func (g *GitService) GetTree(name, ref, path string) (*object.Tree, error) {
161 r := g.configRepo.GetByName(name)
162 if r == nil {
163 return nil, ErrRepositoryNotFound
164 }
165
166 repo, err := git.OpenRepository(r.Path)
167 if err != nil {
168 return nil, err
169 }
170 err = repo.SetRef(ref)
171 if err != nil {
172 return nil, err
173 }
174
175 return repo.Tree(path)
176}
177
178func (g *GitService) IsBinary(name, ref, path string) (bool, error) {
179 r := g.configRepo.GetByName(name)
180 if r == nil {
181 return false, ErrRepositoryNotFound
182 }
183
184 repo, err := git.OpenRepository(r.Path)
185 if err != nil {
186 return false, err
187 }
188 err = repo.SetRef(ref)
189 if err != nil {
190 return false, err
191 }
192
193 return repo.IsBinary(path)
194}
195
196func (g *GitService) GetFileContent(name, ref, path string) ([]byte, error) {
197 r := g.configRepo.GetByName(name)
198 if r == nil {
199 return nil, ErrRepositoryNotFound
200 }
201
202 repo, err := git.OpenRepository(r.Path)
203 if err != nil {
204 return nil, err
205 }
206 err = repo.SetRef(ref)
207 if err != nil {
208 return nil, err
209 }
210
211 return repo.FileContent(path)
212}
213
214func (g *GitService) GetAbout(name string) ([]byte, error) {
215 r := g.configRepo.GetByName(name)
216 if r == nil {
217 return nil, ErrRepositoryNotFound
218 }
219
220 repo, err := git.OpenRepository(r.Path)
221 if err != nil {
222 return nil, err
223 }
224 err = repo.SetRef("")
225 if err != nil {
226 return nil, err
227 }
228
229 file, err := repo.FileContent(r.About)
230 if err != nil {
231 return nil, err
232 }
233
234 return file, nil
235}
236
237func (g *GitService) ListTags(name string) ([]*git.TagReference, error) {
238 r := g.configRepo.GetByName(name)
239 if r == nil {
240 return nil, ErrRepositoryNotFound
241 }
242
243 repo, err := git.OpenRepository(r.Path)
244 if err != nil {
245 return nil, err
246 }
247 return repo.Tags()
248}
249
250func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {
251 r := g.configRepo.GetByName(name)
252 if r == nil {
253 return nil, ErrRepositoryNotFound
254 }
255
256 repo, err := git.OpenRepository(r.Path)
257 if err != nil {
258 return nil, err
259 }
260 return repo.Branches()
261}
262
263func (g *GitService) GetHead(name string) (*plumbing.Reference, error) {
264 r := g.configRepo.GetByName(name)
265 if r == nil {
266 return nil, ErrRepositoryNotFound
267 }
268
269 repo, err := git.OpenRepository(r.Path)
270 if err != nil {
271 return nil, err
272 }
273
274 return repo.Head()
275}