1package git
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io"
8 "path/filepath"
9 "strings"
10
11 "github.com/charmbracelet/log"
12 "github.com/charmbracelet/soft-serve/git"
13 "github.com/go-git/go-git/v5/plumbing/format/pktline"
14)
15
16var (
17
18 // ErrNotAuthed represents unauthorized access.
19 ErrNotAuthed = errors.New("you are not authorized to do this")
20
21 // ErrSystemMalfunction represents a general system error returned to clients.
22 ErrSystemMalfunction = errors.New("something went wrong")
23
24 // ErrInvalidRepo represents an attempt to access a non-existent repo.
25 ErrInvalidRepo = errors.New("invalid repo")
26
27 // ErrInvalidRequest represents an invalid request.
28 ErrInvalidRequest = errors.New("invalid request")
29
30 // ErrMaxConnections represents a maximum connection limit being reached.
31 ErrMaxConnections = errors.New("too many connections, try again later")
32
33 // ErrTimeout is returned when the maximum read timeout is exceeded.
34 ErrTimeout = errors.New("I/O timeout reached")
35)
36
37// WritePktline encodes and writes a pktline to the given writer.
38func WritePktline(w io.Writer, v ...interface{}) {
39 msg := fmt.Sprintln(v...)
40 pkt := pktline.NewEncoder(w)
41 if err := pkt.EncodeString(msg); err != nil {
42 log.Debugf("git: error writing pkt-line message: %s", err)
43 }
44 if err := pkt.Flush(); err != nil {
45 log.Debugf("git: error flushing pkt-line message: %s", err)
46 }
47}
48
49// EnsureWithin ensures the given repo is within the repos directory.
50func EnsureWithin(reposDir string, repo string) error {
51 repoDir := filepath.Join(reposDir, repo)
52 absRepos, err := filepath.Abs(reposDir)
53 if err != nil {
54 log.Debugf("failed to get absolute path for repo: %s", err)
55 return ErrSystemMalfunction
56 }
57 absRepo, err := filepath.Abs(repoDir)
58 if err != nil {
59 log.Debugf("failed to get absolute path for repos: %s", err)
60 return ErrSystemMalfunction
61 }
62
63 // ensure the repo is within the repos directory
64 if !strings.HasPrefix(absRepo, absRepos) {
65 log.Debugf("repo path is outside of repos directory: %s", absRepo)
66 return ErrInvalidRepo
67 }
68
69 return nil
70}
71
72func EnsureDefaultBranch(ctx context.Context, scmd ServiceCommand) error {
73 r, err := git.Open(scmd.Dir)
74 if err != nil {
75 return err
76 }
77 brs, err := r.Branches()
78 if err != nil {
79 return err
80 }
81 if len(brs) == 0 {
82 return fmt.Errorf("no branches found")
83 }
84 // Rename the default branch to the first branch available
85 _, err = r.HEAD()
86 if err == git.ErrReferenceNotExist {
87 branch := brs[0]
88 // Prefer "main" or "master" as the default branch
89 for _, b := range brs {
90 if b == "main" || b == "master" {
91 branch = b
92 break
93 }
94 }
95
96 cmd := git.NewCommand("branch", "-M", branch).WithContext(ctx)
97 if err := cmd.RunInDirWithOptions(scmd.Dir, git.RunInDirOptions{
98 Stdin: scmd.Stdin,
99 Stdout: scmd.Stdout,
100 Stderr: scmd.Stderr,
101 }); err != nil {
102 return err
103 }
104 }
105 if err != nil && err != git.ErrReferenceNotExist {
106 return err
107 }
108 return nil
109}