diff --git a/server/backend/file/file.go b/server/backend/file/file.go index 68e5d92ff701dcadc50ba3939b66e26f560cda51..f0a72f3b38a43d4165d5bee40eb5da08c21f9b18 100644 --- a/server/backend/file/file.go +++ b/server/backend/file/file.go @@ -26,6 +26,7 @@ import ( "io/fs" "os" "path/filepath" + "strconv" "strings" "github.com/charmbracelet/log" @@ -38,15 +39,16 @@ import ( // sub file and directory names. const ( - anonAccess = "anon-access" - allowKeyless = "allow-keyless" - admins = "admins" - repos = "repos" - collabs = "collaborators" - description = "description" - exportOk = "git-daemon-export-ok" - private = "private" - settings = "settings" + anonAccess = "anon-access" + defaultBranch = "default-branch" + allowKeyless = "allow-keyless" + admins = "admins" + repos = "repos" + collabs = "collaborators" + description = "description" + exportOk = "git-daemon-export-ok" + private = "private" + settings = "settings" ) var ( @@ -86,7 +88,8 @@ func (fb *FileBackend) adminsPath() string { } func (fb *FileBackend) collabsPath(repo string) string { - return filepath.Join(fb.path, collabs, repo, collabs) + repo = utils.SanitizeRepo(repo) + ".git" + return filepath.Join(fb.reposPath(), repo, collabs) } func readOneLine(path string) (string, error) { @@ -125,7 +128,7 @@ func NewFileBackend(path string) (*FileBackend, error) { } } - for _, file := range []string{admins, anonAccess, allowKeyless} { + for _, file := range []string{admins, anonAccess, allowKeyless, defaultBranch} { fp := filepath.Join(fb.settingsPath(), file) _, err := os.Stat(fp) if errors.Is(err, fs.ErrNotExist) { @@ -444,6 +447,26 @@ func (fb *FileBackend) AnonAccess() backend.AccessLevel { } } +// DefaultBranch returns the default branch for new repositories. +// +// It implements backend.Backend. +func (fb *FileBackend) DefaultBranch() string { + line, err := readOneLine(filepath.Join(fb.settingsPath(), defaultBranch)) + if err != nil { + logger.Debug("failed to read default-branch file", "err", err) + return defaults[defaultBranch] + } + + return line +} + +// SetDefaultBranch sets the default branch for new repositories. +// +// It implements backend.Backend. +func (fb *FileBackend) SetDefaultBranch(branch string) error { + return os.WriteFile(filepath.Join(fb.settingsPath(), defaultBranch), []byte(branch), 0600) +} + // Description returns the description of the given repo. // // It implements backend.Backend. @@ -531,28 +554,14 @@ func (fb *FileBackend) IsPrivate(repo string) bool { // // It implements backend.Backend. func (fb *FileBackend) SetAllowKeyless(allow bool) error { - f, err := os.OpenFile(filepath.Join(fb.settingsPath(), allowKeyless), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return fmt.Errorf("failed to open allow-keyless file: %w", err) - } - - defer f.Close() //nolint:errcheck - _, err = fmt.Fprintln(f, allow) - return err + return os.WriteFile(filepath.Join(fb.settingsPath(), allowKeyless), []byte(strconv.FormatBool(allow)), 0600) } // SetAnonAccess sets the anonymous access level. // // It implements backend.Backend. func (fb *FileBackend) SetAnonAccess(level backend.AccessLevel) error { - f, err := os.OpenFile(filepath.Join(fb.settingsPath(), anonAccess), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return fmt.Errorf("failed to open anon-access file: %w", err) - } - - defer f.Close() //nolint:errcheck - _, err = fmt.Fprintln(f, level.String()) - return err + return os.WriteFile(filepath.Join(fb.settingsPath(), anonAccess), []byte(level.String()), 0600) } // SetDescription sets the description of the given repo. @@ -560,14 +569,7 @@ func (fb *FileBackend) SetAnonAccess(level backend.AccessLevel) error { // It implements backend.Backend. func (fb *FileBackend) SetDescription(repo string, desc string) error { repo = utils.SanitizeRepo(repo) + ".git" - f, err := os.OpenFile(filepath.Join(fb.reposPath(), repo, description), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return fmt.Errorf("failed to open description file: %w", err) - } - - defer f.Close() //nolint:errcheck - _, err = fmt.Fprintln(f, desc) - return err + return os.WriteFile(filepath.Join(fb.reposPath(), repo, description), []byte(desc), 0600) } // SetPrivate sets the private status of the given repo. diff --git a/server/backend/noop/noop.go b/server/backend/noop/noop.go deleted file mode 100644 index db7324bc0b60fe7fa006b70d1d530e94358446af..0000000000000000000000000000000000000000 --- a/server/backend/noop/noop.go +++ /dev/null @@ -1,141 +0,0 @@ -package noop - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/charmbracelet/soft-serve/git" - "github.com/charmbracelet/soft-serve/server/backend" - "golang.org/x/crypto/ssh" -) - -var ErrNotImpl = fmt.Errorf("not implemented") - -var _ backend.Backend = (*Noop)(nil) - -// Noop is a backend that does nothing. It's used for testing. -type Noop struct { - Port string -} - -// Admins implements backend.Backend -func (*Noop) Admins() ([]string, error) { - return nil, nil -} - -// Collaborators implements backend.Backend -func (*Noop) Collaborators(repo string) ([]string, error) { - return nil, nil -} - -// RemoveAdmin implements backend.Backend -func (*Noop) RemoveAdmin(pk ssh.PublicKey) error { - return nil -} - -// RemoveCollaborator implements backend.Backend -func (*Noop) RemoveCollaborator(pk ssh.PublicKey, repo string) error { - return nil -} - -// AccessLevel implements backend.AccessMethod -func (*Noop) AccessLevel(repo string, pk ssh.PublicKey) backend.AccessLevel { - return backend.AdminAccess -} - -// AddAdmin implements backend.Backend -func (*Noop) AddAdmin(pk ssh.PublicKey, memo string) error { - return ErrNotImpl -} - -// AddCollaborator implements backend.Backend -func (*Noop) AddCollaborator(pk ssh.PublicKey, memo string, repo string) error { - return ErrNotImpl -} - -// AllowKeyless implements backend.Backend -func (*Noop) AllowKeyless() bool { - return true -} - -// AnonAccess implements backend.Backend -func (*Noop) AnonAccess() backend.AccessLevel { - return backend.AdminAccess -} - -// CreateRepository implements backend.Backend -func (*Noop) CreateRepository(name string, private bool) (backend.Repository, error) { - temp, err := os.MkdirTemp("", "soft-serve") - if err != nil { - return nil, err - } - - rp := filepath.Join(temp, name) - _, err = git.Init(rp, private) - if err != nil { - return nil, err - } - - return &repo{path: rp}, nil -} - -// DeleteRepository implements backend.Backend -func (*Noop) DeleteRepository(name string) error { - return ErrNotImpl -} - -// Description implements backend.Backend -func (*Noop) Description(repo string) string { - return "" -} - -// IsAdmin implements backend.Backend -func (*Noop) IsAdmin(pk ssh.PublicKey) bool { - return true -} - -// IsCollaborator implements backend.Backend -func (*Noop) IsCollaborator(pk ssh.PublicKey, repo string) bool { - return true -} - -// IsPrivate implements backend.Backend -func (*Noop) IsPrivate(repo string) bool { - return false -} - -// RenameRepository implements backend.Backend -func (*Noop) RenameRepository(oldName string, newName string) error { - return ErrNotImpl -} - -// Repositories implements backend.Backend -func (*Noop) Repositories() ([]backend.Repository, error) { - return nil, ErrNotImpl -} - -// Repository implements backend.Backend -func (*Noop) Repository(repo string) (backend.Repository, error) { - return nil, ErrNotImpl -} - -// SetAllowKeyless implements backend.Backend -func (*Noop) SetAllowKeyless(allow bool) error { - return ErrNotImpl -} - -// SetAnonAccess implements backend.Backend -func (*Noop) SetAnonAccess(level backend.AccessLevel) error { - return ErrNotImpl -} - -// SetDescription implements backend.Backend -func (*Noop) SetDescription(repo string, desc string) error { - return ErrNotImpl -} - -// SetPrivate implements backend.Backend -func (*Noop) SetPrivate(repo string, priv bool) error { - return ErrNotImpl -} diff --git a/server/backend/noop/repo.go b/server/backend/noop/repo.go deleted file mode 100644 index 12d5394c0bd93c4ee328389600d10f922b4e501e..0000000000000000000000000000000000000000 --- a/server/backend/noop/repo.go +++ /dev/null @@ -1,32 +0,0 @@ -package noop - -import ( - "github.com/charmbracelet/soft-serve/git" - "github.com/charmbracelet/soft-serve/server/backend" -) - -var _ backend.Repository = (*repo)(nil) - -type repo struct { - path string -} - -// Description implements backend.Repository -func (*repo) Description() string { - return "" -} - -// IsPrivate implements backend.Repository -func (*repo) IsPrivate() bool { - return false -} - -// Name implements backend.Repository -func (*repo) Name() string { - return "" -} - -// Repository implements backend.Repository -func (r *repo) Open() (*git.Repository, error) { - return git.Open(r.path) -} diff --git a/server/backend/server.go b/server/backend/server.go index 72198c018d445afabc5b2a5e049e742894c1fb5c..2f0c54a497a3aefce6167f11ba75c37a961931df 100644 --- a/server/backend/server.go +++ b/server/backend/server.go @@ -10,4 +10,8 @@ type ServerBackend interface { AllowKeyless() bool // SetAllowKeyless sets whether or not keyless access is allowed. SetAllowKeyless(allow bool) error + // DefaultBranch returns the default branch for new repositories. + DefaultBranch() string + // SetDefaultBranch sets the default branch for new repositories. + SetDefaultBranch(branch string) error }