Skip to content

Commit

Permalink
feat(server): use smart http git backend
Browse files Browse the repository at this point in the history
This implements the smart http git protocol which also supports
git-receive-pack service.
  • Loading branch information
aymanbagabas committed May 15, 2023
1 parent 0ce60bc commit ae7bdda
Show file tree
Hide file tree
Showing 14 changed files with 816 additions and 352 deletions.
3 changes: 3 additions & 0 deletions cmd/soft/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ func init() {
func main() {
logger := NewDefaultLogger()

// Set global logger
log.SetDefault(logger)

// Set the max number of processes to the number of CPUs
// This is useful when running soft serve in a container
if _, err := maxprocs.Set(maxprocs.Logger(logger.Debugf)); err != nil {
Expand Down
7 changes: 0 additions & 7 deletions git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,6 @@ func (r *Repository) CommitsByPage(ref *Reference, page, size int) (Commits, err
return commits, nil
}

// UpdateServerInfo updates the repository server info.
func (r *Repository) UpdateServerInfo() error {
cmd := git.NewCommand("update-server-info")
_, err := cmd.RunInDir(r.Path)
return err
}

// Config returns the config value for the given key.
func (r *Repository) Config(key string, opts ...ConfigOptions) (string, error) {
dir, err := gitDir(r.Repository)
Expand Down
18 changes: 18 additions & 0 deletions git/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package git

import (
"context"

"github.com/gogs/git-module"
)

// UpdateServerInfo updates the server info file for the given repo path.
func UpdateServerInfo(ctx context.Context, path string) error {
if !isGitDir(path) {
return ErrNotAGitRepository
}

cmd := git.NewCommand("update-server-info").WithContext(ctx).WithTimeout(-1)
_, err := cmd.RunInDir(path)
return err
}
23 changes: 23 additions & 0 deletions git/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package git

import (
"os"
"path/filepath"

"github.com/gobwas/glob"
Expand Down Expand Up @@ -49,3 +50,25 @@ func LatestFile(repo *Repository, pattern string) (string, string, error) {
}
return "", "", ErrFileNotFound
}

// Returns true if path is a directory containing an `objects` directory and a
// `HEAD` file.
func isGitDir(path string) bool {
stat, err := os.Stat(filepath.Join(path, "objects"))
if err != nil {
return false
}
if !stat.IsDir() {
return false
}

stat, err = os.Stat(filepath.Join(path, "HEAD"))
if err != nil {
return false
}
if stat.IsDir() {
return false
}

return true
}
4 changes: 4 additions & 0 deletions internal/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func NewDefaultLogger() *log.Logger {

if debug, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_DEBUG")); debug {
logger.SetLevel(log.DebugLevel)

if verbose, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_VERBOSE")); verbose {
logger.SetReportCaller(true)
}
}

logger.SetTimeFormat(cfg.Log.TimeFormat)
Expand Down
24 changes: 0 additions & 24 deletions server/backend/sqlite/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,6 @@ func (d *SqliteBackend) PostUpdate(stdout io.Writer, stderr io.Writer, repo stri

var wg sync.WaitGroup

// Update server info
wg.Add(1)
go func() {
defer wg.Done()
if err := updateServerInfo(d, repo); err != nil {
d.logger.Error("error updating server-info", "repo", repo, "err", err)
return
}
}()

// Populate last-modified file.
wg.Add(1)
go func() {
Expand All @@ -59,20 +49,6 @@ func (d *SqliteBackend) PostUpdate(stdout io.Writer, stderr io.Writer, repo stri
wg.Wait()
}

func updateServerInfo(d *SqliteBackend, repo string) error {
rr, err := d.Repository(repo)
if err != nil {
return err
}

r, err := rr.Open()
if err != nil {
return err
}

return r.UpdateServerInfo()
}

func populateLastModified(d *SqliteBackend, repo string) error {
var rr *Repo
_rr, err := d.Repository(repo)
Expand Down
7 changes: 1 addition & 6 deletions server/backend/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,17 +151,12 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt
return err
}

rr, err := git.Init(rp, true)
_, err := git.Init(rp, true)
if err != nil {
d.logger.Debug("failed to create repository", "err", err)
return err
}

if err := rr.UpdateServerInfo(); err != nil {
d.logger.Debug("failed to update server info", "err", err)
return err
}

return nil
}); err != nil {
d.logger.Debug("failed to create repository in database", "err", err)
Expand Down
2 changes: 2 additions & 0 deletions server/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func EnsureWithin(reposDir string, repo string) error {
return nil
}

// EnsureDefaultBranch ensures the repo has a default branch.
// It will prefer choosing "main" or "master" if available.
func EnsureDefaultBranch(ctx context.Context, scmd ServiceCommand) error {
r, err := git.Open(scmd.Dir)
if err != nil {
Expand Down
108 changes: 79 additions & 29 deletions server/git/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os/exec"
"strings"

"github.com/charmbracelet/log"
"golang.org/x/sync/errgroup"
)

Expand Down Expand Up @@ -66,58 +67,107 @@ func gitServiceHandler(ctx context.Context, svc Service, scmd ServiceCommand) er
scmd.CmdFunc(cmd)
}

stdin, err := cmd.StdinPipe()
if err != nil {
return err
var (
err error
stdin io.WriteCloser
stdout io.ReadCloser
stderr io.ReadCloser
)

if scmd.Stdin != nil {
stdin, err = cmd.StdinPipe()
if err != nil {
return err
}
}

stdout, err := cmd.StdoutPipe()
if err != nil {
return err
if scmd.Stdout != nil {
stdout, err = cmd.StdoutPipe()
if err != nil {
return err
}
}

stderr, err := cmd.StderrPipe()
if err != nil {
return err
if scmd.Stderr != nil {
stderr, err = cmd.StderrPipe()
if err != nil {
return err
}
}

log.Debugf("git service command in %q: %s", cmd.Dir, cmd.String())
if err := cmd.Start(); err != nil {
return err
}

errg, ctx := errgroup.WithContext(ctx)

// stdin
errg.Go(func() error {
defer stdin.Close() // nolint: errcheck
_, err := io.Copy(stdin, scmd.Stdin)
return err
})
if scmd.Stdin != nil {
errg.Go(func() error {
if scmd.StdinHandler != nil {
return scmd.StdinHandler(scmd.Stdin, stdin)
} else {
return defaultStdinHandler(scmd.Stdin, stdin)
}
})
}

// stdout
errg.Go(func() error {
_, err := io.Copy(scmd.Stdout, stdout)
return err
})
if scmd.Stdout != nil {
errg.Go(func() error {
if scmd.StdoutHandler != nil {
return scmd.StdoutHandler(scmd.Stdout, stdout)
} else {
return defaultStdoutHandler(scmd.Stdout, stdout)
}
})
}

// stderr
errg.Go(func() error {
_, err := io.Copy(scmd.Stderr, stderr)
return err
})
if scmd.Stderr != nil {
errg.Go(func() error {
if scmd.StderrHandler != nil {
return scmd.StderrHandler(scmd.Stderr, stderr)
} else {
return defaultStderrHandler(scmd.Stderr, stderr)
}
})
}

return errors.Join(errg.Wait(), cmd.Wait())
}

// ServiceCommand is used to run a git service command.
type ServiceCommand struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Dir string
Env []string
Args []string
CmdFunc func(*exec.Cmd)
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Dir string
Env []string
Args []string

// Modifier functions
CmdFunc func(*exec.Cmd)
StdinHandler func(io.Reader, io.WriteCloser) error
StdoutHandler func(io.Writer, io.ReadCloser) error
StderrHandler func(io.Writer, io.ReadCloser) error
}

func defaultStdinHandler(in io.Reader, stdin io.WriteCloser) error {
defer stdin.Close() // nolint: errcheck
_, err := io.Copy(stdin, in)
return err
}

func defaultStdoutHandler(out io.Writer, stdout io.ReadCloser) error {
_, err := io.Copy(out, stdout)
return err
}

func defaultStderrHandler(err io.Writer, stderr io.ReadCloser) error {
_, erro := io.Copy(err, stderr)
return erro
}

// UploadPack runs the git upload-pack protocol against the provided repo.
Expand Down
Loading

0 comments on commit ae7bdda

Please sign in to comment.