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