Monorepo for Tangled
1package xrpc
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "net/http"
8
9 securejoin "github.com/cyphar/filepath-securejoin"
10 "tangled.org/core/api/tangled"
11 "tangled.org/core/knotserver/git"
12 "tangled.org/core/patchutil"
13 xrpcerr "tangled.org/core/xrpc/errors"
14)
15
16func (x *Xrpc) MergeCheck(w http.ResponseWriter, r *http.Request) {
17 l := x.Logger.With("handler", "MergeCheck")
18 fail := func(e xrpcerr.XrpcError) {
19 l.Error("failed", "kind", e.Tag, "error", e.Message)
20 writeError(w, e, http.StatusBadRequest)
21 }
22
23 var data tangled.RepoMergeCheck_Input
24 if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
25 fail(xrpcerr.GenericError(err))
26 return
27 }
28
29 did := data.Did
30 name := data.Name
31
32 if did == "" || name == "" {
33 fail(xrpcerr.GenericError(fmt.Errorf("did and name are required")))
34 return
35 }
36
37 relativeRepoPath, err := securejoin.SecureJoin(did, name)
38 if err != nil {
39 fail(xrpcerr.GenericError(err))
40 return
41 }
42
43 repoPath, err := securejoin.SecureJoin(x.Config.Repo.ScanPath, relativeRepoPath)
44 if err != nil {
45 fail(xrpcerr.GenericError(err))
46 return
47 }
48
49 gr, err := git.Open(repoPath, data.Branch)
50 if err != nil {
51 fail(xrpcerr.GenericError(fmt.Errorf("failed to open repository: %w", err)))
52 return
53 }
54
55 mo := git.MergeOptions{}
56 mo.CommitterName = x.Config.Git.UserName
57 mo.CommitterEmail = x.Config.Git.UserEmail
58 mo.FormatPatch = patchutil.IsFormatPatch(data.Patch)
59
60 err = gr.MergeCheckWithOptions(data.Patch, data.Branch, mo)
61
62 response := tangled.RepoMergeCheck_Output{
63 Is_conflicted: false,
64 }
65
66 if err != nil {
67 var mergeErr *git.ErrMerge
68 if errors.As(err, &mergeErr) {
69 response.Is_conflicted = true
70
71 conflicts := make([]*tangled.RepoMergeCheck_ConflictInfo, len(mergeErr.Conflicts))
72 for i, conflict := range mergeErr.Conflicts {
73 conflicts[i] = &tangled.RepoMergeCheck_ConflictInfo{
74 Filename: conflict.Filename,
75 Reason: conflict.Reason,
76 }
77 }
78 response.Conflicts = conflicts
79
80 if mergeErr.Message != "" {
81 response.Message = &mergeErr.Message
82 }
83 } else {
84 response.Is_conflicted = true
85 errMsg := err.Error()
86 response.Error = &errMsg
87 }
88 }
89
90 l.Debug("merge check response", "isConflicted", response.Is_conflicted, "err", response.Error, "conflicts", response.Conflicts)
91
92 w.Header().Set("Content-Type", "application/json")
93 w.WriteHeader(http.StatusOK)
94 json.NewEncoder(w).Encode(response)
95}