1package handler
2
3import (
4 "encoding/json"
5 "fmt"
6 "net/http"
7 "os/exec"
8 "path"
9
10 "github.com/bluesky-social/indigo/atproto/syntax"
11 "github.com/go-git/go-git/v5"
12 "github.com/go-git/go-git/v5/plumbing"
13 "tangled.org/core/api/tangled"
14 "tangled.org/core/knot2/config"
15 "tangled.org/core/log"
16 xrpcerr "tangled.org/core/xrpc/errors"
17)
18
19func XrpcGitKeepCommit(cfg *config.Config) http.HandlerFunc {
20 return func(w http.ResponseWriter, r *http.Request) {
21 ctx := r.Context()
22 l := log.FromContext(ctx).With("handler", "XrpcGitKeepCommit")
23
24 // TODO: get session did
25 actorDid := syntax.DID("")
26
27 var input tangled.GitKeepCommit_Input
28 if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
29 l.Error("failed to decode body", "err", err)
30 panic("unimplemented")
31 }
32
33 repoAt, err := syntax.ParseATURI(input.Repo)
34 if err != nil {
35 l.Error("failed to decode body", "err", err)
36 panic("unimplemented")
37 }
38 repoPath := repoPathFromAtUri(cfg, repoAt)
39
40 // ensure repo exist (if not, clone it)
41 repo, err := git.PlainOpen(repoPath)
42 if err != nil {
43 // TODO: clone the ref from source repo if repo doesn't exist in this knot yet
44 l.Info("repo missing in knot", "err", err)
45 panic("unimplemented")
46 }
47
48 commitId, err := repo.ResolveRevision(plumbing.Revision(input.Ref))
49 if err != nil {
50 l.Error("failed to resolve revision", "ref", input.Ref, "err", err)
51 panic("unimplemented")
52 }
53
54 // set keep-ref for given commit
55 refspec := fmt.Sprintf("refs/knot/%s/keep/%s", actorDid, commitId)
56 updateRefCmd := exec.Command("git", "-C", repoPath, "update-ref", refspec, commitId.String())
57 if err := updateRefCmd.Run(); err != nil {
58 writeError(w, xrpcerr.GenericError(err), http.StatusBadRequest)
59 return
60 }
61
62 output := tangled.GitKeepCommit_Output{
63 CommitId: commitId.String(),
64 }
65
66 w.WriteHeader(http.StatusOK)
67 writeJson(w, output)
68 }
69}
70
71func repoPathFromAtUri(cfg *config.Config, repoAt syntax.ATURI) string {
72 return path.Join(cfg.RepoDir, repoAt.Authority().String(), repoAt.RecordKey().String())
73}
74
75func writeError(w http.ResponseWriter, e xrpcerr.XrpcError, status int) {
76 w.Header().Set("Content-Type", "application/json")
77 w.WriteHeader(status)
78 json.NewEncoder(w).Encode(e)
79}
80
81func writeJson(w http.ResponseWriter, response any) {
82 w.Header().Set("Content-Type", "application/json")
83 if err := json.NewEncoder(w).Encode(response); err != nil {
84 writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError)
85 return
86 }
87}