this repo has no description

appview/{pulls,repo,state}: use xrpc + service auth for knot requests

This now covers: Fork, ForkSync, NewHiddenRef, Merge, MergeCheck,
and RepoCreate/Delete.

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

Changed files
+295 -176
appview
pulls
repo
state
+135 -87
appview/pulls/pulls.go
··· 2 3 import ( 4 "database/sql" 5 - "encoding/json" 6 "errors" 7 "fmt" 8 - "io" 9 "log" 10 "net/http" 11 "sort" ··· 25 "tangled.sh/tangled.sh/core/patchutil" 26 "tangled.sh/tangled.sh/core/tid" 27 "tangled.sh/tangled.sh/core/types" 28 29 "github.com/bluekeyes/go-gitdiff/gitdiff" 30 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 96 return 97 } 98 99 - mergeCheckResponse := s.mergeCheck(f, pull, stack) 100 resubmitResult := pages.Unknown 101 if user.Did == pull.OwnerDid { 102 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 161 } 162 } 163 164 - mergeCheckResponse := s.mergeCheck(f, pull, stack) 165 resubmitResult := pages.Unknown 166 if user != nil && user.Did == pull.OwnerDid { 167 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 226 }) 227 } 228 229 - func (s *Pulls) mergeCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) types.MergeCheckResponse { 230 if pull.State == db.PullMerged { 231 return types.MergeCheckResponse{} 232 } 233 234 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 235 if err != nil { 236 - log.Printf("failed to get registration key: %v", err) 237 return types.MergeCheckResponse{ 238 - Error: "failed to check merge status: this knot is unregistered", 239 - } 240 - } 241 - 242 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 243 - if err != nil { 244 - log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) 245 - return types.MergeCheckResponse{ 246 - Error: "failed to check merge status", 247 } 248 } 249 ··· 257 patch = mergeable.CombinedPatch() 258 } 259 260 - resp, err := ksClient.MergeCheck([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch) 261 if err != nil { 262 - log.Println("failed to check for mergeability:", err) 263 - return types.MergeCheckResponse{ 264 - Error: "failed to check merge status", 265 - } 266 - } 267 - switch resp.StatusCode { 268 - case 404: 269 - return types.MergeCheckResponse{ 270 - Error: "failed to check merge status: this knot does not support PRs", 271 } 272 - case 400: 273 return types.MergeCheckResponse{ 274 - Error: "failed to check merge status: does this knot support PRs?", 275 } 276 } 277 278 - respBody, err := io.ReadAll(resp.Body) 279 - if err != nil { 280 - log.Println("failed to read merge check response body") 281 - return types.MergeCheckResponse{ 282 - Error: "failed to check merge status: knot is not speaking the right language", 283 } 284 } 285 - defer resp.Body.Close() 286 287 - var mergeCheckResponse types.MergeCheckResponse 288 - err = json.Unmarshal(respBody, &mergeCheckResponse) 289 - if err != nil { 290 - log.Println("failed to unmarshal merge check response", err) 291 - return types.MergeCheckResponse{ 292 - Error: "failed to check merge status: knot is not speaking the right language", 293 - } 294 } 295 296 - return mergeCheckResponse 297 } 298 299 func (s *Pulls) resubmitCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult { ··· 923 return 924 } 925 926 - secret, err := db.GetRegistrationKey(s.db, fork.Knot) 927 if err != nil { 928 - log.Println("failed to fetch registration key:", err) 929 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 930 - return 931 - } 932 - 933 - sc, err := knotclient.NewSignedClient(fork.Knot, secret, s.config.Core.Dev) 934 - if err != nil { 935 - log.Println("failed to create signed client:", err) 936 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 937 return 938 } ··· 944 return 945 } 946 947 - resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch) 948 if err != nil { 949 - log.Println("failed to create hidden ref:", err, resp.StatusCode) 950 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 951 return 952 } 953 954 - switch resp.StatusCode { 955 - case 404: 956 - case 400: 957 - s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.") 958 return 959 } 960 ··· 1524 return 1525 } 1526 1527 - secret, err := db.GetRegistrationKey(s.db, forkRepo.Knot) 1528 - if err != nil { 1529 - log.Printf("failed to get registration key for %s: %s", forkRepo.Knot, err) 1530 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1531 - return 1532 - } 1533 - 1534 // update the hidden tracking branch to latest 1535 - signedClient, err := knotclient.NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev) 1536 if err != nil { 1537 - log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) 1538 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1539 return 1540 } 1541 1542 - resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch) 1543 - if err != nil || resp.StatusCode != http.StatusNoContent { 1544 - log.Printf("failed to update tracking branch: %s", err) 1545 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1546 return 1547 } ··· 1958 1959 patch := pullsToMerge.CombinedPatch() 1960 1961 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 1962 if err != nil { 1963 - log.Printf("no registration key found for domain %s: %s\n", f.Knot, err) 1964 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1965 return 1966 } ··· 1977 log.Printf("failed to get primary email: %s", err) 1978 } 1979 1980 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 1981 - if err != nil { 1982 - log.Printf("failed to create signed client for %s: %s", f.Knot, err) 1983 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1984 - return 1985 } 1986 1987 - // Merge the pull request 1988 - resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address) 1989 - if err != nil { 1990 - log.Printf("failed to merge pull request: %s", err) 1991 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1992 - return 1993 } 1994 1995 - if resp.StatusCode != http.StatusOK { 1996 - log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode) 1997 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1998 return 1999 } 2000
··· 2 3 import ( 4 "database/sql" 5 "errors" 6 "fmt" 7 "log" 8 "net/http" 9 "sort" ··· 23 "tangled.sh/tangled.sh/core/patchutil" 24 "tangled.sh/tangled.sh/core/tid" 25 "tangled.sh/tangled.sh/core/types" 26 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 27 28 "github.com/bluekeyes/go-gitdiff/gitdiff" 29 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 95 return 96 } 97 98 + mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 99 resubmitResult := pages.Unknown 100 if user.Did == pull.OwnerDid { 101 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 160 } 161 } 162 163 + mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 164 resubmitResult := pages.Unknown 165 if user != nil && user.Did == pull.OwnerDid { 166 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 225 }) 226 } 227 228 + func (s *Pulls) mergeCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) types.MergeCheckResponse { 229 if pull.State == db.PullMerged { 230 return types.MergeCheckResponse{} 231 } 232 233 + client, err := s.oauth.ServiceClient( 234 + r, 235 + oauth.WithService(f.Knot), 236 + oauth.WithLxm(tangled.RepoMergeCheckNSID), 237 + oauth.WithDev(s.config.Core.Dev), 238 + ) 239 if err != nil { 240 + log.Printf("failed to connect to knot server: %v", err) 241 return types.MergeCheckResponse{ 242 + Error: "failed to check merge status: could not connect to knot server", 243 } 244 } 245 ··· 253 patch = mergeable.CombinedPatch() 254 } 255 256 + resp, err := tangled.RepoMergeCheck( 257 + r.Context(), 258 + client, 259 + &tangled.RepoMergeCheck_Input{ 260 + Did: f.OwnerDid(), 261 + Name: f.RepoName, 262 + Branch: pull.TargetBranch, 263 + Patch: patch, 264 + }, 265 + ) 266 if err != nil { 267 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 268 + if parseErr != nil { 269 + log.Printf("failed to check for mergeability: %v", err) 270 + return types.MergeCheckResponse{ 271 + Error: "failed to check merge status", 272 + } 273 } 274 + log.Printf("failed to check for mergeability: %s", xe.Error()) 275 return types.MergeCheckResponse{ 276 + Error: fmt.Sprintf("failed to check merge status: %s", xe.Message), 277 } 278 } 279 280 + // convert xrpc response to internal types 281 + conflicts := make([]types.ConflictInfo, len(resp.Conflicts)) 282 + for i, conflict := range resp.Conflicts { 283 + conflicts[i] = types.ConflictInfo{ 284 + Filename: conflict.Filename, 285 + Reason: conflict.Reason, 286 } 287 } 288 289 + result := types.MergeCheckResponse{ 290 + IsConflicted: resp.Is_conflicted, 291 + Conflicts: conflicts, 292 } 293 294 + if resp.Message != nil { 295 + result.Message = *resp.Message 296 + } 297 + 298 + if resp.Error != nil { 299 + result.Error = *resp.Error 300 + } 301 + 302 + return result 303 } 304 305 func (s *Pulls) resubmitCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult { ··· 929 return 930 } 931 932 + client, err := s.oauth.ServiceClient( 933 + r, 934 + oauth.WithService(fork.Knot), 935 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 936 + oauth.WithDev(s.config.Core.Dev), 937 + ) 938 if err != nil { 939 + log.Printf("failed to connect to knot server: %v", err) 940 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 941 return 942 } ··· 948 return 949 } 950 951 + resp, err := tangled.RepoHiddenRef( 952 + r.Context(), 953 + client, 954 + &tangled.RepoHiddenRef_Input{ 955 + ForkRef: sourceBranch, 956 + RemoteRef: targetBranch, 957 + Repo: fork.AtUri, 958 + }, 959 + ) 960 if err != nil { 961 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 962 + if parseErr != nil { 963 + log.Printf("failed to create hidden ref: %v", err) 964 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 965 + } else { 966 + log.Printf("failed to create hidden ref: %s", xe.Error()) 967 + if xe.Tag == "AccessControl" { 968 + s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.") 969 + } else { 970 + s.pages.Notice(w, "pull", fmt.Sprintf("Failed to create pull request: %s", xe.Message)) 971 + } 972 + } 973 return 974 } 975 976 + if !resp.Success { 977 + errorMsg := "Failed to create pull request" 978 + if resp.Error != nil { 979 + errorMsg = fmt.Sprintf("Failed to create pull request: %s", *resp.Error) 980 + } 981 + s.pages.Notice(w, "pull", errorMsg) 982 return 983 } 984 ··· 1548 return 1549 } 1550 1551 // update the hidden tracking branch to latest 1552 + client, err := s.oauth.ServiceClient( 1553 + r, 1554 + oauth.WithService(forkRepo.Knot), 1555 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 1556 + oauth.WithDev(s.config.Core.Dev), 1557 + ) 1558 if err != nil { 1559 + log.Printf("failed to connect to knot server: %v", err) 1560 return 1561 } 1562 1563 + resp, err := tangled.RepoHiddenRef( 1564 + r.Context(), 1565 + client, 1566 + &tangled.RepoHiddenRef_Input{ 1567 + ForkRef: pull.PullSource.Branch, 1568 + RemoteRef: pull.TargetBranch, 1569 + Repo: forkRepo.AtUri, 1570 + }, 1571 + ) 1572 + if err != nil || !resp.Success { 1573 + if err != nil { 1574 + log.Printf("failed to update tracking branch: %s", err) 1575 + } else { 1576 + log.Printf("failed to update tracking branch: success=false") 1577 + } 1578 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1579 return 1580 } ··· 1991 1992 patch := pullsToMerge.CombinedPatch() 1993 1994 + client, err := s.oauth.ServiceClient( 1995 + r, 1996 + oauth.WithService(f.Knot), 1997 + oauth.WithLxm(tangled.RepoMergeNSID), 1998 + oauth.WithDev(s.config.Core.Dev), 1999 + ) 2000 if err != nil { 2001 + log.Printf("failed to connect to knot server: %v", err) 2002 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 2003 return 2004 } ··· 2015 log.Printf("failed to get primary email: %s", err) 2016 } 2017 2018 + authorName := ident.Handle.String() 2019 + mergeInput := &tangled.RepoMerge_Input{ 2020 + Did: f.OwnerDid(), 2021 + Name: f.RepoName, 2022 + Branch: pull.TargetBranch, 2023 + Patch: patch, 2024 + CommitMessage: &pull.Title, 2025 + AuthorName: &authorName, 2026 + } 2027 + 2028 + if pull.Body != "" { 2029 + mergeInput.CommitBody = &pull.Body 2030 } 2031 2032 + if email.Address != "" { 2033 + mergeInput.AuthorEmail = &email.Address 2034 } 2035 2036 + err = tangled.RepoMerge(r.Context(), client, mergeInput) 2037 + if err != nil { 2038 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 2039 + if parseErr != nil { 2040 + log.Printf("failed to merge pull request: %v", err) 2041 + s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 2042 + } else { 2043 + log.Printf("failed to merge pull request: %s", xe.Error()) 2044 + s.pages.Notice(w, "pull-merge-error", fmt.Sprintf("Failed to merge pull request: %s", xe.Message)) 2045 + } 2046 return 2047 } 2048
+29 -4
appview/repo/index.go
··· 9 "sort" 10 "strings" 11 12 "tangled.sh/tangled.sh/core/appview/commitverify" 13 "tangled.sh/tangled.sh/core/appview/db" 14 "tangled.sh/tangled.sh/core/appview/oauth" ··· 118 119 var forkInfo *types.ForkInfo 120 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 121 - forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient) 122 if err != nil { 123 log.Printf("Failed to fetch fork information: %v", err) 124 return ··· 231 } 232 233 func getForkInfo( 234 repoInfo repoinfo.RepoInfo, 235 rp *Repo, 236 f *reporesolver.ResolvedRepo, ··· 270 return &forkInfo, nil 271 } 272 273 - newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref) 274 - if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent { 275 - log.Printf("failed to update tracking branch: %s", err) 276 return nil, err 277 } 278 279 hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
··· 9 "sort" 10 "strings" 11 12 + "tangled.sh/tangled.sh/core/api/tangled" 13 "tangled.sh/tangled.sh/core/appview/commitverify" 14 "tangled.sh/tangled.sh/core/appview/db" 15 "tangled.sh/tangled.sh/core/appview/oauth" ··· 119 120 var forkInfo *types.ForkInfo 121 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 122 + forkInfo, err = getForkInfo(r, repoInfo, rp, f, user, signedClient) 123 if err != nil { 124 log.Printf("Failed to fetch fork information: %v", err) 125 return ··· 232 } 233 234 func getForkInfo( 235 + r *http.Request, 236 repoInfo repoinfo.RepoInfo, 237 rp *Repo, 238 f *reporesolver.ResolvedRepo, ··· 272 return &forkInfo, nil 273 } 274 275 + client, err := rp.oauth.ServiceClient( 276 + r, 277 + oauth.WithService(f.Knot), 278 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 279 + oauth.WithDev(rp.config.Core.Dev), 280 + ) 281 + if err != nil { 282 + log.Printf("failed to connect to knot server: %v", err) 283 return nil, err 284 + } 285 + 286 + resp, err := tangled.RepoHiddenRef( 287 + r.Context(), 288 + client, 289 + &tangled.RepoHiddenRef_Input{ 290 + ForkRef: f.Ref, 291 + RemoteRef: f.Ref, 292 + Repo: string(f.RepoAt), 293 + }, 294 + ) 295 + if err != nil || !resp.Success { 296 + if err != nil { 297 + log.Printf("failed to update tracking branch: %s", err) 298 + } else { 299 + log.Printf("failed to update tracking branch: success=false") 300 + } 301 + return nil, fmt.Errorf("failed to update tracking branch") 302 } 303 304 hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
+105 -66
appview/repo/repo.go
··· 17 "strings" 18 "time" 19 20 "tangled.sh/tangled.sh/core/api/tangled" 21 "tangled.sh/tangled.sh/core/appview/commitverify" 22 "tangled.sh/tangled.sh/core/appview/config" ··· 33 "tangled.sh/tangled.sh/core/rbac" 34 "tangled.sh/tangled.sh/core/tid" 35 "tangled.sh/tangled.sh/core/types" 36 37 securejoin "github.com/cyphar/filepath-securejoin" 38 "github.com/go-chi/chi/v5" 39 "github.com/go-git/go-git/v5/plumbing" 40 41 - comatproto "github.com/bluesky-social/indigo/api/atproto" 42 "github.com/bluesky-social/indigo/atproto/syntax" 43 - lexutil "github.com/bluesky-social/indigo/lex/util" 44 ) 45 46 type Repo struct { ··· 54 enforcer *rbac.Enforcer 55 notifier notify.Notifier 56 logger *slog.Logger 57 } 58 59 func New( ··· 915 } 916 log.Println("removed repo record ", f.RepoAt.String()) 917 918 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 919 if err != nil { 920 - log.Printf("no key found for domain %s: %s\n", f.Knot, err) 921 return 922 } 923 924 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 925 if err != nil { 926 - log.Println("failed to create client to ", f.Knot) 927 - return 928 - } 929 - 930 - ksResp, err := ksClient.RemoveRepo(f.OwnerDid(), f.RepoName) 931 - if err != nil { 932 - log.Printf("failed to make request to %s: %s", f.Knot, err) 933 - return 934 - } 935 - 936 - if ksResp.StatusCode != http.StatusNoContent { 937 - log.Println("failed to remove repo from knot, continuing anyway ", f.Knot) 938 } else { 939 log.Println("removed repo from knot ", f.Knot) 940 } ··· 1010 return 1011 } 1012 1013 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1014 if err != nil { 1015 - log.Printf("no key found for domain %s: %s\n", f.Knot, err) 1016 return 1017 } 1018 1019 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 1020 if err != nil { 1021 - log.Println("failed to create client to ", f.Knot) 1022 - return 1023 - } 1024 - 1025 - ksResp, err := ksClient.SetDefaultBranch(f.OwnerDid(), f.RepoName, branch) 1026 - if err != nil { 1027 - log.Printf("failed to make request to %s: %s", f.Knot, err) 1028 - return 1029 - } 1030 - 1031 - if ksResp.StatusCode != http.StatusNoContent { 1032 - rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 1033 return 1034 } 1035 ··· 1323 1324 switch r.Method { 1325 case http.MethodPost: 1326 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1327 if err != nil { 1328 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot)) 1329 return 1330 } 1331 1332 - client, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 1333 - if err != nil { 1334 - rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1335 return 1336 } 1337 1338 - var uri string 1339 - if rp.config.Core.Dev { 1340 - uri = "http" 1341 - } else { 1342 - uri = "https" 1343 - } 1344 - forkName := fmt.Sprintf("%s", f.RepoName) 1345 - forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.RepoName) 1346 - 1347 - _, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref) 1348 if err != nil { 1349 - rp.pages.Notice(w, "repo", "Failed to sync repository fork.") 1350 return 1351 } 1352 ··· 1409 // repo with this name already exists, append random string 1410 forkName = fmt.Sprintf("%s-%s", forkName, randomString(3)) 1411 } 1412 - secret, err := db.GetRegistrationKey(rp.db, knot) 1413 - if err != nil { 1414 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) 1415 - return 1416 - } 1417 1418 - client, err := knotclient.NewSignedClient(knot, secret, rp.config.Core.Dev) 1419 if err != nil { 1420 - rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1421 return 1422 } 1423 ··· 1453 } 1454 }() 1455 1456 - resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName) 1457 if err != nil { 1458 - rp.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1459 - return 1460 - } 1461 1462 - switch resp.StatusCode { 1463 - case http.StatusConflict: 1464 - rp.pages.Notice(w, "repo", "A repository with that name already exists.") 1465 return 1466 - case http.StatusInternalServerError: 1467 - rp.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 1468 - case http.StatusNoContent: 1469 - // continue 1470 } 1471 1472 xrpcClient, err := rp.oauth.AuthorizedClient(r)
··· 17 "strings" 18 "time" 19 20 + comatproto "github.com/bluesky-social/indigo/api/atproto" 21 + lexutil "github.com/bluesky-social/indigo/lex/util" 22 "tangled.sh/tangled.sh/core/api/tangled" 23 "tangled.sh/tangled.sh/core/appview/commitverify" 24 "tangled.sh/tangled.sh/core/appview/config" ··· 35 "tangled.sh/tangled.sh/core/rbac" 36 "tangled.sh/tangled.sh/core/tid" 37 "tangled.sh/tangled.sh/core/types" 38 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 39 + "tangled.sh/tangled.sh/core/xrpc/serviceauth" 40 41 securejoin "github.com/cyphar/filepath-securejoin" 42 "github.com/go-chi/chi/v5" 43 "github.com/go-git/go-git/v5/plumbing" 44 45 "github.com/bluesky-social/indigo/atproto/syntax" 46 ) 47 48 type Repo struct { ··· 56 enforcer *rbac.Enforcer 57 notifier notify.Notifier 58 logger *slog.Logger 59 + serviceAuth *serviceauth.ServiceAuth 60 } 61 62 func New( ··· 918 } 919 log.Println("removed repo record ", f.RepoAt.String()) 920 921 + client, err := rp.oauth.ServiceClient( 922 + r, 923 + oauth.WithService(f.Knot), 924 + oauth.WithLxm(tangled.RepoDeleteNSID), 925 + oauth.WithDev(rp.config.Core.Dev), 926 + ) 927 if err != nil { 928 + log.Println("failed to connect to knot server:", err) 929 return 930 } 931 932 + err = tangled.RepoDelete( 933 + r.Context(), 934 + client, 935 + &tangled.RepoDelete_Input{ 936 + Did: f.OwnerDid(), 937 + Name: f.RepoName, 938 + }, 939 + ) 940 if err != nil { 941 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 942 + if parseErr != nil { 943 + log.Printf("failed to delete repo from knot %s: %s", f.Knot, err) 944 + } else { 945 + log.Printf("failed to delete repo from knot %s: %s", f.Knot, xe.Error()) 946 + } 947 + // Continue anyway since we want to clean up local state 948 } else { 949 log.Println("removed repo from knot ", f.Knot) 950 } ··· 1020 return 1021 } 1022 1023 + client, err := rp.oauth.ServiceClient( 1024 + r, 1025 + oauth.WithService(f.Knot), 1026 + oauth.WithLxm(tangled.RepoSetDefaultBranchNSID), 1027 + oauth.WithDev(rp.config.Core.Dev), 1028 + ) 1029 if err != nil { 1030 + log.Println("failed to connect to knot server:", err) 1031 + rp.pages.Notice(w, "repo-settings", "Failed to connect to knot server.") 1032 return 1033 } 1034 1035 + err = tangled.RepoSetDefaultBranch( 1036 + r.Context(), 1037 + client, 1038 + &tangled.RepoSetDefaultBranch_Input{ 1039 + Repo: fmt.Sprintf("%s/%s", f.OwnerDid(), f.RepoName), 1040 + DefaultBranch: branch, 1041 + }, 1042 + ) 1043 if err != nil { 1044 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 1045 + if parseErr != nil { 1046 + log.Printf("failed to set default branch: %s", err) 1047 + rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 1048 + } else { 1049 + log.Printf("failed to set default branch: %s", xe.Error()) 1050 + rp.pages.Notice(w, "repo-settings", fmt.Sprintf("Failed to set default branch: %s", xe.Message)) 1051 + } 1052 return 1053 } 1054 ··· 1342 1343 switch r.Method { 1344 case http.MethodPost: 1345 + client, err := rp.oauth.ServiceClient( 1346 + r, 1347 + oauth.WithService(f.Knot), 1348 + oauth.WithLxm(tangled.RepoForkSyncNSID), 1349 + oauth.WithDev(rp.config.Core.Dev), 1350 + ) 1351 if err != nil { 1352 + rp.pages.Notice(w, "repo", "Failed to connect to knot server.") 1353 return 1354 } 1355 1356 + repoInfo := f.RepoInfo(user) 1357 + if repoInfo.Source == nil { 1358 + rp.pages.Notice(w, "repo", "This repository is not a fork.") 1359 return 1360 } 1361 1362 + err = tangled.RepoForkSync( 1363 + r.Context(), 1364 + client, 1365 + &tangled.RepoForkSync_Input{ 1366 + Did: user.Did, 1367 + Name: f.RepoName, 1368 + Source: repoInfo.Source.AtUri, 1369 + Branch: f.Ref, 1370 + }, 1371 + ) 1372 if err != nil { 1373 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 1374 + if parseErr != nil { 1375 + log.Printf("failed to sync repository fork: %s", err) 1376 + rp.pages.Notice(w, "repo", "Failed to sync repository fork.") 1377 + } else { 1378 + log.Printf("failed to sync repository fork: %s", xe.Error()) 1379 + rp.pages.Notice(w, "repo", fmt.Sprintf("Failed to sync repository fork: %s", xe.Message)) 1380 + } 1381 return 1382 } 1383 ··· 1440 // repo with this name already exists, append random string 1441 forkName = fmt.Sprintf("%s-%s", forkName, randomString(3)) 1442 } 1443 + client, err := rp.oauth.ServiceClient( 1444 + r, 1445 + oauth.WithService(knot), 1446 + oauth.WithLxm(tangled.RepoForkNSID), 1447 + oauth.WithDev(rp.config.Core.Dev), 1448 + ) 1449 1450 if err != nil { 1451 + log.Printf("error creating client for knot server: %v", err) 1452 + rp.pages.Notice(w, "repo", "Failed to connect to knot server.") 1453 return 1454 } 1455 ··· 1485 } 1486 }() 1487 1488 + err = tangled.RepoFork( 1489 + r.Context(), 1490 + client, 1491 + &tangled.RepoFork_Input{ 1492 + Did: user.Did, 1493 + Name: &forkName, 1494 + Source: forkSourceUrl, 1495 + }, 1496 + ) 1497 + 1498 if err != nil { 1499 + xe, err := xrpcerr.Unmarshal(err.Error()) 1500 + if err != nil { 1501 + log.Println(err) 1502 + rp.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1503 + return 1504 + } 1505 1506 + log.Println(xe.Error()) 1507 + rp.pages.Notice(w, "repo", fmt.Sprintf("Failed to create repository on knot server: %s.", xe.Message)) 1508 return 1509 } 1510 1511 xrpcClient, err := rp.oauth.AuthorizedClient(r)
+26 -19
appview/state/state.go
··· 28 "tangled.sh/tangled.sh/core/eventconsumer" 29 "tangled.sh/tangled.sh/core/idresolver" 30 "tangled.sh/tangled.sh/core/jetstream" 31 - "tangled.sh/tangled.sh/core/knotclient" 32 tlog "tangled.sh/tangled.sh/core/log" 33 "tangled.sh/tangled.sh/core/rbac" 34 "tangled.sh/tangled.sh/core/tid" 35 ) 36 37 type State struct { ··· 329 330 existingRepo, err := db.GetRepo(s.db, user.Did, repoName) 331 if err == nil && existingRepo != nil { 332 - s.pages.Notice(w, "repo", fmt.Sprintf("A repo by this name already exists on %s", existingRepo.Knot)) 333 return 334 } 335 336 - secret, err := db.GetRegistrationKey(s.db, domain) 337 - if err != nil { 338 - s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", domain)) 339 - return 340 - } 341 342 - client, err := knotclient.NewSignedClient(domain, secret, s.config.Core.Dev) 343 if err != nil { 344 s.pages.Notice(w, "repo", "Failed to connect to knot server.") 345 return ··· 394 } 395 }() 396 397 - resp, err := client.NewRepo(user.Did, repoName, defaultBranch) 398 if err != nil { 399 - s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 400 - return 401 - } 402 403 - switch resp.StatusCode { 404 - case http.StatusConflict: 405 - s.pages.Notice(w, "repo", "A repository with that name already exists.") 406 return 407 - case http.StatusInternalServerError: 408 - s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 409 - case http.StatusNoContent: 410 - // continue 411 } 412 413 repo.AtUri = atresp.Uri
··· 28 "tangled.sh/tangled.sh/core/eventconsumer" 29 "tangled.sh/tangled.sh/core/idresolver" 30 "tangled.sh/tangled.sh/core/jetstream" 31 tlog "tangled.sh/tangled.sh/core/log" 32 "tangled.sh/tangled.sh/core/rbac" 33 "tangled.sh/tangled.sh/core/tid" 34 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 35 ) 36 37 type State struct { ··· 329 330 existingRepo, err := db.GetRepo(s.db, user.Did, repoName) 331 if err == nil && existingRepo != nil { 332 + s.pages.Notice(w, "repo", fmt.Sprintf("You already have a repository by this name on %s", existingRepo.Knot)) 333 return 334 } 335 336 + client, err := s.oauth.ServiceClient( 337 + r, 338 + oauth.WithService(domain), 339 + oauth.WithLxm(tangled.RepoCreateNSID), 340 + oauth.WithDev(s.config.Core.Dev), 341 + ) 342 343 if err != nil { 344 s.pages.Notice(w, "repo", "Failed to connect to knot server.") 345 return ··· 394 } 395 }() 396 397 + err = tangled.RepoCreate( 398 + r.Context(), 399 + client, 400 + &tangled.RepoCreate_Input{ 401 + Default_branch: &defaultBranch, 402 + Did: user.Did, 403 + Name: repoName, 404 + }, 405 + ) 406 + 407 if err != nil { 408 + xe, err := xrpcerr.Unmarshal(err.Error()) 409 + if err != nil { 410 + log.Println(err) 411 + s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 412 + return 413 + } 414 415 + log.Println(xe.Error()) 416 + s.pages.Notice(w, "repo", fmt.Sprintf("Failed to create repository on knot server: %s.", xe.Message)) 417 return 418 } 419 420 repo.AtUri = atresp.Uri