Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
2.9 kB · 111 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112package xrpc
import ( "encoding/json" "errors" "fmt" "net/http"
"github.com/bluesky-social/indigo/atproto/syntax" "tangled.org/core/api/tangled" "tangled.org/core/knotserver/git" "tangled.org/core/patchutil" "tangled.org/core/rbac" "tangled.org/core/types" xrpcerr "tangled.org/core/xrpc/errors")
func (x *Xrpc) Merge(w http.ResponseWriter, r *http.Request) { l := x.Logger.With("handler", "Merge") fail := func(e xrpcerr.XrpcError) { l.Error("failed", "kind", e.Tag, "error", e.Message) writeError(w, e, http.StatusBadRequest) }
actorDid, ok := r.Context().Value(ActorDid).(syntax.DID) if !ok { fail(xrpcerr.MissingActorDidError) return }
var data tangled.RepoMerge_Input if err := json.NewDecoder(r.Body).Decode(&data); err != nil { fail(xrpcerr.GenericError(err)) return }
did := data.Did name := data.Name
if did == "" || name == "" { fail(xrpcerr.GenericError(fmt.Errorf("did and name are required"))) return }
repoDid, repoPath, err := x.resolveRepoDID(data.Repo, did, name) if err != nil { l.Error("failed to resolve repo", "err", err) fail(xrpcerr.RepoNotFoundError) return }
if ok, err := x.Enforcer.IsPushAllowed(actorDid.String(), rbac.ThisServer, repoDid.String()); !ok || err != nil { l.Error("insufficient permissions", "did", actorDid.String(), "repo", repoDid.String()) writeError(w, xrpcerr.AccessControlError(actorDid.String()), http.StatusUnauthorized) return }
gr, err := git.Open(repoPath, data.Branch) if err != nil { fail(xrpcerr.GenericError(fmt.Errorf("failed to open repository: %w", err))) return } if x.Sandbox != nil { gr = gr.WithSandbox(x.Sandbox) }
mo := git.MergeOptions{} if data.AuthorName != nil { mo.AuthorName = *data.AuthorName } if data.AuthorEmail != nil { mo.AuthorEmail = *data.AuthorEmail } if data.CommitBody != nil { mo.CommitBody = *data.CommitBody } if data.CommitMessage != nil { mo.CommitMessage = *data.CommitMessage }
mo.CommitterName = x.Config.Git.UserName mo.CommitterEmail = x.Config.Git.UserEmail mo.FormatPatch = patchutil.IsFormatPatch(data.Patch)
err = gr.MergeWithOptions(data.Patch, data.Branch, mo) if err != nil { var mergeErr *git.ErrMerge if errors.As(err, &mergeErr) { conflicts := make([]types.ConflictInfo, len(mergeErr.Conflicts)) for i, conflict := range mergeErr.Conflicts { conflicts[i] = types.ConflictInfo{ Filename: conflict.Filename, Reason: conflict.Reason, } }
conflictErr := xrpcerr.NewXrpcError( xrpcerr.WithTag("MergeConflict"), xrpcerr.WithMessage(fmt.Sprintf("Merge failed due to conflicts: %s", mergeErr.Message)), ) writeError(w, conflictErr, http.StatusConflict) return } else { l.Error("failed to merge", "error", err.Error()) writeError(w, xrpcerr.GitError(err), http.StatusInternalServerError) return } }
w.WriteHeader(http.StatusOK)}