···
"github.com/go-chi/chi/v5"
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/types"
comatproto "github.com/bluesky-social/indigo/api/atproto"
lexutil "github.com/bluesky-social/indigo/lex/util"
···
func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult {
-
if pull.State == db.PullMerged {
-
if pull.PullSource == nil {
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
-
log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err)
-
resp, err := us.Branch(f.OwnerDid(), f.RepoName, pull.PullSource.Branch)
log.Println("failed to reach knotserver", err)
···
body, err := io.ReadAll(resp.Body)
-
log.Printf("Error reading response body: %v", err)
var result types.RepoBranchResponse
-
err = json.Unmarshal(body, &result)
log.Println("failed to parse response:", err)
-
if pull.Submissions[pull.LastRoundNumber()].SourceRev != result.Branch.Hash {
-
log.Println(pull.Submissions[pull.LastRoundNumber()].SourceRev, result.Branch.Hash)
return pages.ShouldResubmit
-
return pages.ShouldNotResubmit
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
···
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
···
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
RepoInfo: f.RepoInfo(s, user),
Branches: result.Branches,
-
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
title := r.FormValue("title")
body := r.FormValue("body")
targetBranch := r.FormValue("targetBranch")
sourceBranch := r.FormValue("sourceBranch")
patch := r.FormValue("patch")
-
isBranchBased := isPushAllowed && (sourceBranch != "")
isPatchBased := patch != ""
-
if !isBranchBased && !isPatchBased {
s.pages.Notice(w, "pull", "Neither source branch nor patch supplied.")
···
-
// TODO: check if knot has this capability
-
var pullSource *db.PullSource
-
var recordPullSource *tangled.RepoPull_Source
-
pullSource = &db.PullSource{
-
recordPullSource = &tangled.RepoPull_Source{
-
// generate a patch using /compare
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
-
switch resp.StatusCode {
-
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
-
respBody, err := io.ReadAll(resp.Body)
-
log.Println("failed to compare across branches")
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
defer resp.Body.Close()
-
var diffTreeResponse types.RepoDiffTreeResponse
-
err = json.Unmarshal(respBody, &diffTreeResponse)
-
log.Println("failed to unmarshal diff tree response", err)
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
sourceRev = diffTreeResponse.DiffTree.Rev2
-
patch = diffTreeResponse.DiffTree.Patch
-
// Validate patch format
-
if !isPatchValid(patch) {
-
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
-
tx, err := s.db.BeginTx(r.Context(), nil)
-
log.Println("failed to start tx")
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
initialSubmission := db.PullSubmission{
-
err = db.NewPull(tx, &db.Pull{
-
TargetBranch: targetBranch,
-
Submissions: []*db.PullSubmission{
-
PullSource: pullSource,
-
log.Println("failed to create pull request", err)
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
client, _ := s.auth.AuthorizedClient(r)
-
pullId, err := db.NextPullId(s.db, f.RepoAt)
-
log.Println("failed to get pull id", err)
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
-
Collection: tangled.RepoPullNSID,
-
Record: &lexutil.LexiconTypeDecoder{
-
Val: &tangled.RepoPull{
-
TargetRepo: string(f.RepoAt),
-
TargetBranch: targetBranch,
-
Source: recordPullSource,
-
err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri)
-
log.Println("failed to get pull id", err)
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
···
var recordPullSource *tangled.RepoPull_Source
-
// this pull is a branch based pull
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
-
if pull.IsSameRepoBranch() && isPushAllowed {
-
sourceBranch := pull.PullSource.Branch
-
targetBranch := pull.TargetBranch
-
recordPullSource = &tangled.RepoPull_Source{
// extract patch by performing compare
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
-
switch resp.StatusCode {
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
-
respBody, err := io.ReadAll(resp.Body)
log.Println("failed to compare across branches")
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
-
defer resp.Body.Close()
var diffTreeResponse types.RepoDiffTreeResponse
err = json.Unmarshal(respBody, &diffTreeResponse)
log.Println("failed to unmarshal diff tree response", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
sourceRev = diffTreeResponse.DiffTree.Rev2
···
-
// Validate patch format
if !isPatchValid(patch) {
s.pages.Notice(w, "resubmit-error", "Invalid patch format. Please provide a valid diff.")
···
"github.com/go-chi/chi/v5"
"tangled.sh/tangled.sh/core/api/tangled"
+
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/types"
comatproto "github.com/bluesky-social/indigo/api/atproto"
+
"github.com/bluesky-social/indigo/atproto/syntax"
lexutil "github.com/bluesky-social/indigo/lex/util"
···
func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult {
+
if pull.State == db.PullMerged || pull.PullSource == nil {
+
var knot, ownerDid, repoName string
+
if pull.PullSource.Repo != nil {
+
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String())
+
log.Println("failed to get source repo", err)
+
ownerDid = sourceRepo.Did
+
repoName = sourceRepo.Name
+
// pulls within the same repo
+
ownerDid = f.OwnerDid()
+
us, err := NewUnsignedClient(knot, s.config.Dev)
+
log.Printf("failed to setup client for %s; ignoring: %v", knot, err)
+
resp, err := us.Branch(ownerDid, repoName, pull.PullSource.Branch)
log.Println("failed to reach knotserver", err)
···
body, err := io.ReadAll(resp.Body)
+
log.Printf("error reading response body: %v", err)
+
defer resp.Body.Close()
var result types.RepoBranchResponse
+
if err := json.Unmarshal(body, &result); err != nil {
log.Println("failed to parse response:", err)
+
latestSubmission := pull.Submissions[pull.LastRoundNumber()]
+
if latestSubmission.SourceRev != result.Branch.Hash {
return pages.ShouldResubmit
+
return pages.ShouldNotResubmit
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
···
+
forks, err := db.GetForksByDid(s.db, user.Did)
+
log.Println("failed to get forks", err)
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
···
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
RepoInfo: f.RepoInfo(s, user),
Branches: result.Branches,
title := r.FormValue("title")
body := r.FormValue("body")
targetBranch := r.FormValue("targetBranch")
+
fromFork := r.FormValue("fromFork")
sourceBranch := r.FormValue("sourceBranch")
patch := r.FormValue("patch")
+
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
+
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
isPatchBased := patch != ""
+
isForkBased := fromFork != "" && sourceBranch != ""
+
if !isBranchBased && !isPatchBased && !isForkBased {
s.pages.Notice(w, "pull", "Neither source branch nor patch supplied.")
···
+
s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch)
+
} else if isPatchBased {
+
s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch)
+
} else if isForkBased {
+
s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch)
+
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {
+
pullSource := &db.PullSource{
+
recordPullSource := &tangled.RepoPull_Source{
+
// Generate a patch using /compare
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
+
switch resp.StatusCode {
+
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
+
respBody, err := io.ReadAll(resp.Body)
+
log.Println("failed to compare across branches")
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
defer resp.Body.Close()
+
var diffTreeResponse types.RepoDiffTreeResponse
+
err = json.Unmarshal(respBody, &diffTreeResponse)
+
log.Println("failed to unmarshal diff tree response", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
sourceRev := diffTreeResponse.DiffTree.Rev2
+
patch := diffTreeResponse.DiffTree.Patch
+
if !isPatchValid(patch) {
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
+
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {
+
if !isPatchValid(patch) {
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
+
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
+
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
+
if errors.Is(err, sql.ErrNoRows) {
+
s.pages.Notice(w, "pull", "No such fork.")
+
log.Println("failed to fetch fork:", err)
+
s.pages.Notice(w, "pull", "Failed to fetch fork.")
+
secret, err := db.GetRegistrationKey(s.db, fork.Knot)
+
log.Println("failed to fetch registration key:", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev)
+
log.Println("failed to create signed client:", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
us, err := NewUnsignedClient(fork.Knot, s.config.Dev)
+
log.Println("failed to create unsigned client:", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch)
+
log.Println("failed to create hidden ref:", err, resp.StatusCode)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
switch resp.StatusCode {
+
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
+
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch))
+
// We're now comparing the sourceBranch (on the fork) against the hiddenRef which is tracking
+
// the targetBranch on the target repository. This code is a bit confusing, but here's an example:
+
// hiddenRef: hidden/feature-1/main (on repo-fork)
+
// targetBranch: main (on repo-1)
+
// sourceBranch: feature-1 (on repo-fork)
+
diffResp, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch)
+
log.Println("failed to compare across branches", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
respBody, err := io.ReadAll(diffResp.Body)
+
log.Println("failed to read response body", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
defer resp.Body.Close()
+
var diffTreeResponse types.RepoDiffTreeResponse
+
err = json.Unmarshal(respBody, &diffTreeResponse)
+
log.Println("failed to unmarshal diff tree response", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
sourceRev := diffTreeResponse.DiffTree.Rev2
+
patch := diffTreeResponse.DiffTree.Patch
+
if !isPatchValid(patch) {
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
+
forkAtUri, err := syntax.ParseATURI(fork.AtUri)
+
log.Println("failed to parse fork AT URI", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{
+
}, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri})
+
func (s *State) createPullRequest(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch, sourceRev string, pullSource *db.PullSource, recordPullSource *tangled.RepoPull_Source) {
+
tx, err := s.db.BeginTx(r.Context(), nil)
+
log.Println("failed to start tx")
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
initialSubmission := db.PullSubmission{
+
err = db.NewPull(tx, &db.Pull{
+
TargetBranch: targetBranch,
+
Submissions: []*db.PullSubmission{
+
PullSource: pullSource,
+
log.Println("failed to create pull request", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
client, _ := s.auth.AuthorizedClient(r)
+
pullId, err := db.NextPullId(s.db, f.RepoAt)
+
log.Println("failed to get pull id", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
+
Collection: tangled.RepoPullNSID,
+
Record: &lexutil.LexiconTypeDecoder{
+
Val: &tangled.RepoPull{
+
TargetRepo: string(f.RepoAt),
+
TargetBranch: targetBranch,
+
Source: recordPullSource,
+
err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri)
+
log.Println("failed to get pull id", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
···
var recordPullSource *tangled.RepoPull_Source
+
var ownerDid, repoName, knotName string
+
var isSameRepo bool = pull.IsSameRepoBranch()
+
sourceBranch := pull.PullSource.Branch
+
targetBranch := pull.TargetBranch
+
recordPullSource = &tangled.RepoPull_Source{
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
+
if isSameRepo && isPushAllowed {
+
ownerDid = f.OwnerDid()
+
} else if !isSameRepo {
+
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String())
+
log.Println("failed to get source repo", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
ownerDid = sourceRepo.Did
+
repoName = sourceRepo.Name
+
knotName = sourceRepo.Knot
+
if sourceBranch != "" && knotName != "" {
// extract patch by performing compare
+
ksClient, err := NewUnsignedClient(knotName, s.config.Dev)
+
log.Printf("failed to create client for %s: %s", knotName, err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
secret, err := db.GetRegistrationKey(s.db, knotName)
+
log.Printf("failed to get registration key for %s: %s", knotName, err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
// update the hidden tracking branch to latest
+
signedClient, err := NewSignedClient(knotName, secret, s.config.Dev)
+
log.Printf("failed to create signed client for %s: %s", knotName, err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
resp, err := signedClient.NewHiddenRef(ownerDid, repoName, sourceBranch, targetBranch)
+
if err != nil || resp.StatusCode != http.StatusNoContent {
+
log.Printf("failed to update tracking branch: %s", err)
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
var compareResp *http.Response
+
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch))
+
compareResp, err = ksClient.Compare(ownerDid, repoName, hiddenRef, sourceBranch)
+
compareResp, err = ksClient.Compare(ownerDid, repoName, targetBranch, sourceBranch)
+
log.Printf("failed to compare branches: %s", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
defer compareResp.Body.Close()
+
switch compareResp.StatusCode {
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
+
respBody, err := io.ReadAll(compareResp.Body)
log.Println("failed to compare across branches")
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
+
defer compareResp.Body.Close()
var diffTreeResponse types.RepoDiffTreeResponse
err = json.Unmarshal(respBody, &diffTreeResponse)
log.Println("failed to unmarshal diff tree response", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
sourceRev = diffTreeResponse.DiffTree.Rev2
···
if !isPatchValid(patch) {
s.pages.Notice(w, "resubmit-error", "Invalid patch format. Please provide a valid diff.")