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) GetTree(name, ref, path string) (*object.Tree, error) {
144 r := g.configRepo.GetByName(name)
145 if r == nil {
146 return nil, ErrRepositoryNotFound
147 }
148
149 repo, err := git.OpenRepository(r.Path)
150 if err != nil {
151 return nil, err
152 }
153 err = repo.SetRef(ref)
154 if err != nil {
155 return nil, err
156 }
157
158 return repo.Tree(path)
159}
160
161func (g *GitService) IsBinary(name, ref, path string) (bool, error) {
162 r := g.configRepo.GetByName(name)
163 if r == nil {
164 return false, ErrRepositoryNotFound
165 }
166
167 repo, err := git.OpenRepository(r.Path)
168 if err != nil {
169 return false, err
170 }
171 err = repo.SetRef(ref)
172 if err != nil {
173 return false, err
174 }
175
176 return repo.IsBinary(path)
177}
178
179func (g *GitService) GetFileContent(name, ref, path string) ([]byte, error) {
180 r := g.configRepo.GetByName(name)
181 if r == nil {
182 return nil, ErrRepositoryNotFound
183 }
184
185 repo, err := git.OpenRepository(r.Path)
186 if err != nil {
187 return nil, err
188 }
189 err = repo.SetRef(ref)
190 if err != nil {
191 return nil, err
192 }
193
194 return repo.FileContent(path)
195}
196
197func (g *GitService) GetAbout(name string) ([]byte, error) {
198 r := g.configRepo.GetByName(name)
199 if r == nil {
200 return nil, ErrRepositoryNotFound
201 }
202
203 repo, err := git.OpenRepository(r.Path)
204 if err != nil {
205 return nil, err
206 }
207 err = repo.SetRef("")
208 if err != nil {
209 return nil, err
210 }
211
212 file, err := repo.FileContent(r.About)
213 if err != nil {
214 return nil, err
215 }
216
217 return file, nil
218}
219
220func (g *GitService) ListTags(name string) ([]*git.TagReference, error) {
221 r := g.configRepo.GetByName(name)
222 if r == nil {
223 return nil, ErrRepositoryNotFound
224 }
225
226 repo, err := git.OpenRepository(r.Path)
227 if err != nil {
228 return nil, err
229 }
230 return repo.Tags()
231}
232
233func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {
234 r := g.configRepo.GetByName(name)
235 if r == nil {
236 return nil, ErrRepositoryNotFound
237 }
238
239 repo, err := git.OpenRepository(r.Path)
240 if err != nil {
241 return nil, err
242 }
243 return repo.Branches()
244}
245
246func (g *GitService) GetHead(name string) (*plumbing.Reference, error) {
247 r := g.configRepo.GetByName(name)
248 if r == nil {
249 return nil, ErrRepositoryNotFound
250 }
251
252 repo, err := git.OpenRepository(r.Path)
253 if err != nil {
254 return nil, err
255 }
256
257 return repo.Head()
258}