knotserver/xrpc: add sh.tangled.repo.deleteBranch #643

merged
opened by oppi.li targeting master from push-rvtqynpmozzy
Changed files
+153
api
knotserver
lexicons
+30
api/tangled/repodeleteBranch.go
···
+
// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT.
+
+
package tangled
+
+
// schema: sh.tangled.repo.deleteBranch
+
+
import (
+
"context"
+
+
"github.com/bluesky-social/indigo/lex/util"
+
)
+
+
const (
+
RepoDeleteBranchNSID = "sh.tangled.repo.deleteBranch"
+
)
+
+
// RepoDeleteBranch_Input is the input argument to a sh.tangled.repo.deleteBranch call.
+
type RepoDeleteBranch_Input struct {
+
Branch string `json:"branch" cborgen:"branch"`
+
Repo string `json:"repo" cborgen:"repo"`
+
}
+
+
// RepoDeleteBranch calls the XRPC method "sh.tangled.repo.deleteBranch".
+
func RepoDeleteBranch(ctx context.Context, c util.LexClient, input *RepoDeleteBranch_Input) error {
+
if err := c.LexDo(ctx, util.Procedure, "application/json", "sh.tangled.repo.deleteBranch", nil, input, nil); err != nil {
+
return err
+
}
+
+
return nil
+
}
+5
knotserver/git/branch.go
···
slices.Reverse(branches)
return branches, nil
}
+
+
func (g *GitRepo) DeleteBranch(branch string) error {
+
ref := plumbing.NewBranchReferenceName(branch)
+
return g.r.Storer.RemoveReference(ref)
+
}
+87
knotserver/xrpc/delete_branch.go
···
+
package xrpc
+
+
import (
+
"encoding/json"
+
"fmt"
+
"net/http"
+
+
comatproto "github.com/bluesky-social/indigo/api/atproto"
+
"github.com/bluesky-social/indigo/atproto/syntax"
+
"github.com/bluesky-social/indigo/xrpc"
+
securejoin "github.com/cyphar/filepath-securejoin"
+
"tangled.org/core/api/tangled"
+
"tangled.org/core/knotserver/git"
+
"tangled.org/core/rbac"
+
+
xrpcerr "tangled.org/core/xrpc/errors"
+
)
+
+
func (x *Xrpc) DeleteBranch(w http.ResponseWriter, r *http.Request) {
+
l := x.Logger
+
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.RepoDeleteBranch_Input
+
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
+
fail(xrpcerr.GenericError(err))
+
return
+
}
+
+
// unfortunately we have to resolve repo-at here
+
repoAt, err := syntax.ParseATURI(data.Repo)
+
if err != nil {
+
fail(xrpcerr.InvalidRepoError(data.Repo))
+
return
+
}
+
+
// resolve this aturi to extract the repo record
+
ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String())
+
if err != nil || ident.Handle.IsInvalidHandle() {
+
fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err)))
+
return
+
}
+
+
xrpcc := xrpc.Client{Host: ident.PDSEndpoint()}
+
resp, err := comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
+
if err != nil {
+
fail(xrpcerr.GenericError(err))
+
return
+
}
+
+
repo := resp.Value.Val.(*tangled.Repo)
+
didPath, err := securejoin.SecureJoin(actorDid.String(), repo.Name)
+
if err != nil {
+
fail(xrpcerr.GenericError(err))
+
return
+
}
+
+
if ok, err := x.Enforcer.IsPushAllowed(actorDid.String(), rbac.ThisServer, didPath); !ok || err != nil {
+
l.Error("insufficent permissions", "did", actorDid.String())
+
writeError(w, xrpcerr.AccessControlError(actorDid.String()), http.StatusUnauthorized)
+
return
+
}
+
+
path, _ := securejoin.SecureJoin(x.Config.Repo.ScanPath, didPath)
+
gr, err := git.PlainOpen(path)
+
if err != nil {
+
fail(xrpcerr.GenericError(err))
+
return
+
}
+
+
err = gr.DeleteBranch(data.Branch)
+
if err != nil {
+
l.Error("deleting branch", "error", err.Error(), "branch", data.Branch)
+
writeError(w, xrpcerr.GitError(err), http.StatusInternalServerError)
+
return
+
}
+
+
w.WriteHeader(http.StatusOK)
+
}
+1
knotserver/xrpc/xrpc.go
···
r.Use(x.ServiceAuth.VerifyServiceAuth)
r.Post("/"+tangled.RepoSetDefaultBranchNSID, x.SetDefaultBranch)
+
r.Post("/"+tangled.RepoDeleteBranchNSID, x.DeleteBranch)
r.Post("/"+tangled.RepoCreateNSID, x.CreateRepo)
r.Post("/"+tangled.RepoDeleteNSID, x.DeleteRepo)
r.Post("/"+tangled.RepoForkStatusNSID, x.ForkStatus)
+30
lexicons/repo/deleteBranch.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.tangled.repo.deleteBranch",
+
"defs": {
+
"main": {
+
"type": "procedure",
+
"description": "Delete a branch on this repository",
+
"input": {
+
"encoding": "application/json",
+
"schema": {
+
"type": "object",
+
"required": [
+
"repo",
+
"branch"
+
],
+
"properties": {
+
"repo": {
+
"type": "string",
+
"format": "at-uri"
+
},
+
"branch": {
+
"type": "string"
+
}
+
}
+
}
+
}
+
}
+
}
+
}
+