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 RepositoryNotFoundErr = 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, RepositoryNotFoundErr
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) WriteTarGZip(w io.Writer, name, ref string, prefix string) error {
98 r := g.configRepo.GetByName(name)
99 if r == nil {
100 return RepositoryNotFoundErr
101 }
102
103 repo, err := git.OpenRepository(r.Path)
104 if err != nil {
105 return err
106 }
107
108 err = repo.SetRef(ref)
109 if err != nil {
110 return err
111 }
112
113 gw := gzip.NewWriter(w)
114 defer gw.Close()
115
116 err = repo.WriteTar(gw, prefix)
117 if err != nil {
118 return err
119 }
120
121 return nil
122}
123
124func (g *GitService) GetTree(name, ref, path string) (*object.Tree, error) {
125 r := g.configRepo.GetByName(name)
126 if r == nil {
127 return nil, RepositoryNotFoundErr
128 }
129
130 repo, err := git.OpenRepository(r.Path)
131 if err != nil {
132 return nil, err
133 }
134 err = repo.SetRef(ref)
135 if err != nil {
136 return nil, err
137 }
138
139 return repo.Tree(path)
140}
141
142func (g *GitService) IsBinary(name, ref, path string) (bool, error) {
143 r := g.configRepo.GetByName(name)
144 if r == nil {
145 return false, RepositoryNotFoundErr
146 }
147
148 repo, err := git.OpenRepository(r.Path)
149 if err != nil {
150 return false, err
151 }
152 err = repo.SetRef(ref)
153 if err != nil {
154 return false, err
155 }
156
157 return repo.IsBinary(path)
158}
159
160func (g *GitService) GetFileContent(name, ref, path string) ([]byte, error) {
161 r := g.configRepo.GetByName(name)
162 if r == nil {
163 return nil, RepositoryNotFoundErr
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.FileContent(path)
176}
177
178func (g *GitService) GetAbout(name string) ([]byte, error) {
179 r := g.configRepo.GetByName(name)
180 if r == nil {
181 return nil, RepositoryNotFoundErr
182 }
183
184 repo, err := git.OpenRepository(r.Path)
185 if err != nil {
186 return nil, err
187 }
188 err = repo.SetRef("")
189 if err != nil {
190 return nil, err
191 }
192
193 return repo.FileContent(r.About)
194}
195
196func (g *GitService) ListTags(name string) ([]*plumbing.Reference, error) {
197 r := g.configRepo.GetByName(name)
198 if r == nil {
199 return nil, RepositoryNotFoundErr
200 }
201
202 repo, err := git.OpenRepository(r.Path)
203 if err != nil {
204 return nil, err
205 }
206 return repo.Tags()
207}
208
209func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {
210 r := g.configRepo.GetByName(name)
211 if r == nil {
212 return nil, RepositoryNotFoundErr
213 }
214
215 repo, err := git.OpenRepository(r.Path)
216 if err != nil {
217 return nil, err
218 }
219 return repo.Branches()
220}
221
222func (g *GitService) GetHead(name string) (*plumbing.Reference, error) {
223 r := g.configRepo.GetByName(name)
224 if r == nil {
225 return nil, RepositoryNotFoundErr
226 }
227
228 repo, err := git.OpenRepository(r.Path)
229 if err != nil {
230 return nil, err
231 }
232
233 return repo.Head()
234}