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