repo.go

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