1package config
2
3import (
4 "log"
5 "os"
6 "path/filepath"
7 "strings"
8
9 "github.com/charmbracelet/soft-serve/git"
10 "github.com/charmbracelet/soft-serve/proto"
11 "github.com/charmbracelet/soft-serve/server/db/types"
12)
13
14var _ proto.Provider = &Config{}
15var _ proto.MetadataProvider = &Config{}
16
17// Metadata returns the repository's metadata.
18func (c *Config) Metadata(name string) (proto.Metadata, error) {
19 i, err := c.db.GetRepo(name)
20 if err != nil {
21 return nil, err
22 }
23 return &repo{
24 cfg: c,
25 info: i,
26 }, nil
27}
28
29// Open opens a repository.
30func (c *Config) Open(name string) (proto.Repository, error) {
31 if name == "" {
32 return nil, os.ErrNotExist
33 }
34 name = strings.TrimSuffix(name, ".git")
35 r, err := git.Open(filepath.Join(c.RepoPath(), name+".git"))
36 if err != nil {
37 log.Printf("error opening repository %q: %v", name, err)
38 return nil, err
39 }
40 return &repo{
41 cfg: c,
42 repo: r,
43 }, nil
44}
45
46// ListRepos lists all repositories metadata.
47func (c *Config) ListRepos() ([]proto.Metadata, error) {
48 md := make([]proto.Metadata, 0)
49 ds, err := os.ReadDir(c.RepoPath())
50 if err != nil {
51 return nil, err
52 }
53 for _, d := range ds {
54 name := strings.TrimSuffix(d.Name(), ".git")
55 r, err := c.db.GetRepo(name)
56 if err != nil || r == nil {
57 md = append(md, &emptyMetadata{
58 name: name,
59 cfg: c,
60 })
61 } else {
62 md = append(md, &repo{
63 cfg: c,
64 info: r,
65 })
66 }
67 }
68 return md, nil
69}
70
71// Create creates a new repository.
72func (c *Config) Create(name string, projectName string, description string, isPrivate bool) error {
73 name = strings.TrimSuffix(name, ".git")
74 name = strings.ToLower(name)
75 if _, err := git.Init(filepath.Join(c.RepoPath(), name+".git"), true); err != nil {
76 return err
77 }
78 if err := c.db.AddRepo(name, projectName, description, isPrivate); err != nil {
79 return err
80 }
81 return nil
82}
83
84// Delete deletes a repository.
85func (c *Config) Delete(name string) error {
86 name = strings.TrimSuffix(name, ".git")
87 if err := os.RemoveAll(filepath.Join(c.RepoPath(), name+".git")); err != nil {
88 return err
89 }
90 if err := c.db.DeleteRepo(name); err != nil {
91 return err
92 }
93 return nil
94}
95
96// Rename renames a repository.
97func (c *Config) Rename(name string, newName string) error {
98 name = strings.TrimSuffix(name, ".git")
99 newName = strings.TrimSuffix(newName, ".git")
100 if err := os.Rename(filepath.Join(c.RepoPath(), name+".git"), filepath.Join(c.RepoPath(), newName+".git")); err != nil {
101 return err
102 }
103 if err := c.db.SetRepoName(name, newName); err != nil {
104 return err
105 }
106 return nil
107}
108
109// SetProjectName sets the repository's project name.
110func (c *Config) SetProjectName(name string, projectName string) error {
111 name = strings.TrimSuffix(name, ".git")
112 if err := c.db.SetRepoProjectName(name, projectName); err != nil {
113 return err
114 }
115 return nil
116}
117
118// SetDescription sets the repository's description.
119func (c *Config) SetDescription(name string, description string) error {
120 name = strings.TrimSuffix(name, ".git")
121 if err := c.db.SetRepoDescription(name, description); err != nil {
122 return err
123 }
124 return nil
125}
126
127// SetPrivate sets the repository's privacy.
128func (c *Config) SetPrivate(name string, isPrivate bool) error {
129 name = strings.TrimSuffix(name, ".git")
130 if err := c.db.SetRepoPrivate(name, isPrivate); err != nil {
131 return err
132 }
133 return nil
134}
135
136// SetDefaultBranch sets the repository's default branch.
137func (c *Config) SetDefaultBranch(name string, branch string) error {
138 re, err := c.Open(name)
139 if err != nil {
140 return err
141 }
142 if _, err = re.Repository().SymbolicRef("HEAD", "refs/heads/"+branch); err != nil {
143 return err
144 }
145 return nil
146}
147
148var _ proto.Metadata = emptyMetadata{}
149
150type emptyMetadata struct {
151 name string
152 cfg *Config
153}
154
155// Collabs implements proto.Metadata.
156func (emptyMetadata) Collabs() []proto.User {
157 return []proto.User{}
158}
159
160// Description implements proto.Metadata.
161func (emptyMetadata) Description() string {
162 return ""
163}
164
165// IsPrivate implements proto.Metadata.
166func (emptyMetadata) IsPrivate() bool {
167 return false
168}
169
170// Name implements proto.Metadata.
171func (e emptyMetadata) Name() string {
172 return e.name
173}
174
175// Open implements proto.Metadata.
176func (e emptyMetadata) Open() (proto.Repository, error) {
177 return e.cfg.Open(e.Name())
178}
179
180// ProjectName implements proto.Metadata.
181func (emptyMetadata) ProjectName() string {
182 return ""
183}
184
185var _ proto.Metadata = &repo{}
186var _ proto.Repository = &repo{}
187
188// repo represents a Git repository.
189type repo struct {
190 cfg *Config
191 repo *git.Repository
192 info *types.Repo
193}
194
195// Open opens the underlying Repository.
196func (r *repo) Open() (proto.Repository, error) {
197 return r.cfg.Open(r.Name())
198}
199
200// Name returns the name of the repository.
201func (r *repo) Name() string {
202 if r.repo != nil {
203 strings.TrimSuffix(filepath.Base(r.repo.Path), ".git")
204 }
205 return r.info.Name
206}
207
208// ProjectName returns the repository's project name.
209func (r *repo) ProjectName() string {
210 return r.info.ProjectName
211}
212
213// Description returns the repository's description.
214func (r *repo) Description() string {
215 return r.info.Description
216}
217
218// IsPrivate returns true if the repository is private.
219func (r *repo) IsPrivate() bool {
220 return r.info.Private
221}
222
223// Collabs returns the repository's collaborators.
224func (r *repo) Collabs() []proto.User {
225 collabs := make([]proto.User, 0)
226 cs, err := r.cfg.db.ListRepoCollabs(r.Name())
227 if err != nil {
228 return collabs
229 }
230 for i, c := range cs {
231 u := &user{
232 cfg: r.cfg,
233 user: c,
234 }
235 collabs[i] = u
236 }
237 return collabs
238}
239
240// Repository returns the underlying git.Repository.
241func (r *repo) Repository() *git.Repository {
242 return r.repo
243}