+13
-3
appview/db/pulls.go
+13
-3
appview/db/pulls.go
···
122
122
return len(p.Submissions) - 1
123
123
}
124
124
125
-
func (p *Pull) IsSameRepoBranch() bool {
125
+
func (p *Pull) IsPatchBased() bool {
126
+
return p.PullSource == nil
127
+
}
128
+
129
+
func (p *Pull) IsBranchBased() bool {
126
130
if p.PullSource != nil {
127
131
if p.PullSource.RepoAt != nil {
128
132
return p.PullSource.RepoAt == &p.RepoAt
···
134
138
return false
135
139
}
136
140
137
-
func (p *Pull) IsPatch() bool {
138
-
return p.PullSource == nil
141
+
func (p *Pull) IsForkBased() bool {
142
+
if p.PullSource != nil {
143
+
if p.PullSource.RepoAt != nil {
144
+
// make sure repos are different
145
+
return p.PullSource.RepoAt != &p.RepoAt
146
+
}
147
+
}
148
+
return false
139
149
}
140
150
141
151
func (s PullSubmission) AsNiceDiff(targetBranch string) types.NiceDiff {
+2
-2
appview/pages/templates/fragments/pullActions.html
+2
-2
appview/pages/templates/fragments/pullActions.html
···
9
9
{{ $isConflicted := and .MergeCheck (or .MergeCheck.Error .MergeCheck.IsConflicted) }}
10
10
{{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }}
11
11
{{ $isLastRound := eq $roundNumber $lastIdx }}
12
-
{{ $isSameRepoBranch := .Pull.IsSameRepoBranch }}
12
+
{{ $isSameRepoBranch := .Pull.IsBranchBased }}
13
13
{{ $isUpToDate := .ResubmitCheck.No }}
14
14
<div class="relative w-fit">
15
15
<div id="actions-{{$roundNumber}}" class="flex flex-wrap gap-2">
···
42
42
{{ $disabled = "disabled" }}
43
43
{{ end }}
44
44
<button id="resubmitBtn"
45
-
{{ if not .Pull.IsPatch }}
45
+
{{ if not .Pull.IsPatchBased }}
46
46
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit"
47
47
{{ else }}
48
48
hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit"
+3
-3
appview/pages/templates/repo/pulls/pull.html
+3
-3
appview/pages/templates/repo/pulls/pull.html
···
45
45
<a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a>
46
46
</span>
47
47
</span>
48
-
{{ if not .Pull.IsPatch }}
48
+
{{ if not .Pull.IsPatchBased }}
49
49
<span>from
50
-
{{ if not .Pull.IsSameRepoBranch }}
50
+
{{ if not .Pull.IsBranchBased }}
51
51
<a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSourceRepo.Name }}</a>
52
52
{{ end }}
53
53
54
54
{{ $fullRepo := .RepoInfo.FullName }}
55
-
{{ if not .Pull.IsSameRepoBranch }}
55
+
{{ if not .Pull.IsBranchBased }}
56
56
{{ $fullRepo = printf "%s/%s" $owner .PullSourceRepo.Name }}
57
57
{{ end }}
58
58
<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">
+2
-2
appview/pages/templates/repo/pulls/pulls.html
+2
-2
appview/pages/templates/repo/pulls/pulls.html
···
78
78
{{ .TargetBranch }}
79
79
</span>
80
80
</span>
81
-
{{ if not .IsPatch }}
81
+
{{ if not .IsPatchBased }}
82
82
<span>from
83
-
{{ if not .IsSameRepoBranch }}
83
+
{{ if not .IsBranchBased }}
84
84
<a href="/{{ $owner }}/{{ .PullSource.Repo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSource.Repo.Name }}</a>
85
85
{{ end }}
86
86
+344
-192
appview/state/pull.go
+344
-192
appview/state/pull.go
···
634
634
return
635
635
}
636
636
637
-
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
638
-
switch resp.StatusCode {
639
-
case 404:
640
-
case 400:
641
-
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
642
-
return
643
-
}
644
-
645
-
respBody, err := io.ReadAll(resp.Body)
637
+
diffTreeResponse, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
646
638
if err != nil {
647
-
log.Println("failed to compare across branches")
648
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
649
-
return
650
-
}
651
-
defer resp.Body.Close()
652
-
653
-
var diffTreeResponse types.RepoDiffTreeResponse
654
-
err = json.Unmarshal(respBody, &diffTreeResponse)
655
-
if err != nil {
656
-
log.Println("failed to unmarshal diff tree response", err)
657
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
639
+
log.Println("failed to compare", err)
640
+
s.pages.Notice(w, "pull", err.Error())
658
641
return
659
642
}
660
643
···
730
713
// hiddenRef: hidden/feature-1/main (on repo-fork)
731
714
// targetBranch: main (on repo-1)
732
715
// sourceBranch: feature-1 (on repo-fork)
733
-
diffResp, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch)
716
+
diffTreeResponse, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch)
734
717
if err != nil {
735
718
log.Println("failed to compare across branches", err)
736
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
737
-
return
738
-
}
739
-
740
-
respBody, err := io.ReadAll(diffResp.Body)
741
-
if err != nil {
742
-
log.Println("failed to read response body", err)
743
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
744
-
return
745
-
}
746
-
747
-
defer resp.Body.Close()
748
-
749
-
var diffTreeResponse types.RepoDiffTreeResponse
750
-
err = json.Unmarshal(respBody, &diffTreeResponse)
751
-
if err != nil {
752
-
log.Println("failed to unmarshal diff tree response", err)
753
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
719
+
s.pages.Notice(w, "pull", err.Error())
754
720
return
755
721
}
756
722
···
1015
981
})
1016
982
return
1017
983
case http.MethodPost:
1018
-
patch := r.FormValue("patch")
1019
-
var sourceRev string
1020
-
var recordPullSource *tangled.RepoPull_Source
1021
-
1022
-
var ownerDid, repoName, knotName string
1023
-
var isSameRepo bool = pull.IsSameRepoBranch()
1024
-
sourceBranch := pull.PullSource.Branch
1025
-
targetBranch := pull.TargetBranch
1026
-
recordPullSource = &tangled.RepoPull_Source{
1027
-
Branch: sourceBranch,
984
+
if pull.IsPatchBased() {
985
+
s.resubmitPatch(w, r)
986
+
return
987
+
} else if pull.IsBranchBased() {
988
+
s.resubmitBranch(w, r)
989
+
return
990
+
} else if pull.IsForkBased() {
991
+
s.resubmitFork(w, r)
992
+
return
1028
993
}
994
+
}
995
+
}
1029
996
1030
-
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
1031
-
if isSameRepo && isPushAllowed {
1032
-
ownerDid = f.OwnerDid()
1033
-
repoName = f.RepoName
1034
-
knotName = f.Knot
1035
-
} else if !isSameRepo {
1036
-
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String())
1037
-
if err != nil {
1038
-
log.Println("failed to get source repo", err)
1039
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1040
-
return
1041
-
}
1042
-
ownerDid = sourceRepo.Did
1043
-
repoName = sourceRepo.Name
1044
-
knotName = sourceRepo.Knot
1045
-
}
997
+
func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) {
998
+
user := s.auth.GetUser(r)
1046
999
1047
-
if sourceBranch != "" && knotName != "" {
1048
-
// extract patch by performing compare
1049
-
ksClient, err := NewUnsignedClient(knotName, s.config.Dev)
1050
-
if err != nil {
1051
-
log.Printf("failed to create client for %s: %s", knotName, err)
1052
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1053
-
return
1054
-
}
1000
+
pull, ok := r.Context().Value("pull").(*db.Pull)
1001
+
if !ok {
1002
+
log.Println("failed to get pull")
1003
+
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1004
+
return
1005
+
}
1055
1006
1056
-
if !isSameRepo {
1057
-
secret, err := db.GetRegistrationKey(s.db, knotName)
1058
-
if err != nil {
1059
-
log.Printf("failed to get registration key for %s: %s", knotName, err)
1060
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1061
-
return
1062
-
}
1063
-
// update the hidden tracking branch to latest
1064
-
signedClient, err := NewSignedClient(knotName, secret, s.config.Dev)
1065
-
if err != nil {
1066
-
log.Printf("failed to create signed client for %s: %s", knotName, err)
1067
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1068
-
return
1069
-
}
1070
-
resp, err := signedClient.NewHiddenRef(ownerDid, repoName, sourceBranch, targetBranch)
1071
-
if err != nil || resp.StatusCode != http.StatusNoContent {
1072
-
log.Printf("failed to update tracking branch: %s", err)
1073
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1074
-
return
1075
-
}
1076
-
}
1007
+
f, err := fullyResolvedRepo(r)
1008
+
if err != nil {
1009
+
log.Println("failed to get repo and knot", err)
1010
+
return
1011
+
}
1077
1012
1078
-
var compareResp *http.Response
1079
-
if !isSameRepo {
1080
-
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch))
1081
-
compareResp, err = ksClient.Compare(ownerDid, repoName, hiddenRef, sourceBranch)
1082
-
} else {
1083
-
compareResp, err = ksClient.Compare(ownerDid, repoName, targetBranch, sourceBranch)
1084
-
}
1085
-
if err != nil {
1086
-
log.Printf("failed to compare branches: %s", err)
1087
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1088
-
return
1089
-
}
1090
-
defer compareResp.Body.Close()
1013
+
if user.Did != pull.OwnerDid {
1014
+
log.Println("unauthorized user")
1015
+
w.WriteHeader(http.StatusUnauthorized)
1016
+
return
1017
+
}
1091
1018
1092
-
switch compareResp.StatusCode {
1093
-
case 404:
1094
-
case 400:
1095
-
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
1096
-
return
1097
-
}
1019
+
patch := r.FormValue("patch")
1098
1020
1099
-
respBody, err := io.ReadAll(compareResp.Body)
1100
-
if err != nil {
1101
-
log.Println("failed to compare across branches")
1102
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1103
-
return
1104
-
}
1105
-
defer compareResp.Body.Close()
1021
+
if err = validateResubmittedPatch(pull, patch); err != nil {
1022
+
s.pages.Notice(w, "resubmit-error", err.Error())
1023
+
}
1106
1024
1107
-
var diffTreeResponse types.RepoDiffTreeResponse
1108
-
err = json.Unmarshal(respBody, &diffTreeResponse)
1109
-
if err != nil {
1110
-
log.Println("failed to unmarshal diff tree response", err)
1111
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1112
-
return
1113
-
}
1025
+
tx, err := s.db.BeginTx(r.Context(), nil)
1026
+
if err != nil {
1027
+
log.Println("failed to start tx")
1028
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1029
+
return
1030
+
}
1031
+
defer tx.Rollback()
1114
1032
1115
-
sourceRev = diffTreeResponse.DiffTree.Rev2
1116
-
patch = diffTreeResponse.DiffTree.Patch
1117
-
}
1033
+
err = db.ResubmitPull(tx, pull, patch, "")
1034
+
if err != nil {
1035
+
log.Println("failed to resubmit pull request", err)
1036
+
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.")
1037
+
return
1038
+
}
1039
+
client, _ := s.auth.AuthorizedClient(r)
1118
1040
1119
-
if patch == "" {
1120
-
s.pages.Notice(w, "resubmit-error", "Patch is empty.")
1121
-
return
1122
-
}
1041
+
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1042
+
if err != nil {
1043
+
// failed to get record
1044
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1045
+
return
1046
+
}
1123
1047
1124
-
if patch == pull.LatestPatch() {
1125
-
s.pages.Notice(w, "resubmit-error", "Patch is identical to previous submission.")
1126
-
return
1127
-
}
1048
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1049
+
Collection: tangled.RepoPullNSID,
1050
+
Repo: user.Did,
1051
+
Rkey: pull.Rkey,
1052
+
SwapRecord: ex.Cid,
1053
+
Record: &lexutil.LexiconTypeDecoder{
1054
+
Val: &tangled.RepoPull{
1055
+
Title: pull.Title,
1056
+
PullId: int64(pull.PullId),
1057
+
TargetRepo: string(f.RepoAt),
1058
+
TargetBranch: pull.TargetBranch,
1059
+
Patch: patch, // new patch
1060
+
},
1061
+
},
1062
+
})
1063
+
if err != nil {
1064
+
log.Println("failed to update record", err)
1065
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
1066
+
return
1067
+
}
1128
1068
1129
-
if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev {
1130
-
s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")
1131
-
return
1132
-
}
1069
+
if err = tx.Commit(); err != nil {
1070
+
log.Println("failed to commit transaction", err)
1071
+
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
1072
+
return
1073
+
}
1133
1074
1134
-
if !isPatchValid(patch) {
1135
-
s.pages.Notice(w, "resubmit-error", "Invalid patch format. Please provide a valid diff.")
1136
-
return
1137
-
}
1075
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1076
+
return
1077
+
}
1138
1078
1139
-
tx, err := s.db.BeginTx(r.Context(), nil)
1140
-
if err != nil {
1141
-
log.Println("failed to start tx")
1142
-
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1143
-
return
1144
-
}
1145
-
defer tx.Rollback()
1079
+
func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) {
1080
+
user := s.auth.GetUser(r)
1146
1081
1147
-
err = db.ResubmitPull(tx, pull, patch, sourceRev)
1148
-
if err != nil {
1149
-
log.Println("failed to create pull request", err)
1150
-
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1151
-
return
1152
-
}
1153
-
client, _ := s.auth.AuthorizedClient(r)
1082
+
pull, ok := r.Context().Value("pull").(*db.Pull)
1083
+
if !ok {
1084
+
log.Println("failed to get pull")
1085
+
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
1086
+
return
1087
+
}
1154
1088
1155
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1156
-
if err != nil {
1157
-
// failed to get record
1158
-
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1159
-
return
1160
-
}
1089
+
f, err := fullyResolvedRepo(r)
1090
+
if err != nil {
1091
+
log.Println("failed to get repo and knot", err)
1092
+
return
1093
+
}
1161
1094
1162
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1163
-
Collection: tangled.RepoPullNSID,
1164
-
Repo: user.Did,
1165
-
Rkey: pull.Rkey,
1166
-
SwapRecord: ex.Cid,
1167
-
Record: &lexutil.LexiconTypeDecoder{
1168
-
Val: &tangled.RepoPull{
1169
-
Title: pull.Title,
1170
-
PullId: int64(pull.PullId),
1171
-
TargetRepo: string(f.RepoAt),
1172
-
TargetBranch: pull.TargetBranch,
1173
-
Patch: patch, // new patch
1174
-
Source: recordPullSource,
1175
-
},
1095
+
if user.Did != pull.OwnerDid {
1096
+
log.Println("unauthorized user")
1097
+
w.WriteHeader(http.StatusUnauthorized)
1098
+
return
1099
+
}
1100
+
1101
+
if !f.RepoInfo(s, user).Roles.IsPushAllowed() {
1102
+
log.Println("unauthorized user")
1103
+
w.WriteHeader(http.StatusUnauthorized)
1104
+
return
1105
+
}
1106
+
1107
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
1108
+
if err != nil {
1109
+
log.Printf("failed to create client for %s: %s", f.Knot, err)
1110
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1111
+
return
1112
+
}
1113
+
1114
+
diffTreeResponse, err := ksClient.Compare(f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.PullSource.Branch)
1115
+
if err != nil {
1116
+
log.Printf("compare request failed: %s", err)
1117
+
s.pages.Notice(w, "resubmit-error", err.Error())
1118
+
return
1119
+
}
1120
+
1121
+
sourceRev := diffTreeResponse.DiffTree.Rev2
1122
+
patch := diffTreeResponse.DiffTree.Patch
1123
+
1124
+
if err = validateResubmittedPatch(pull, patch); err != nil {
1125
+
s.pages.Notice(w, "resubmit-error", err.Error())
1126
+
}
1127
+
1128
+
if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev {
1129
+
s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")
1130
+
return
1131
+
}
1132
+
1133
+
tx, err := s.db.BeginTx(r.Context(), nil)
1134
+
if err != nil {
1135
+
log.Println("failed to start tx")
1136
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1137
+
return
1138
+
}
1139
+
defer tx.Rollback()
1140
+
1141
+
err = db.ResubmitPull(tx, pull, patch, sourceRev)
1142
+
if err != nil {
1143
+
log.Println("failed to create pull request", err)
1144
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1145
+
return
1146
+
}
1147
+
client, _ := s.auth.AuthorizedClient(r)
1148
+
1149
+
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1150
+
if err != nil {
1151
+
// failed to get record
1152
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1153
+
return
1154
+
}
1155
+
1156
+
recordPullSource := &tangled.RepoPull_Source{
1157
+
Branch: pull.PullSource.Branch,
1158
+
}
1159
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1160
+
Collection: tangled.RepoPullNSID,
1161
+
Repo: user.Did,
1162
+
Rkey: pull.Rkey,
1163
+
SwapRecord: ex.Cid,
1164
+
Record: &lexutil.LexiconTypeDecoder{
1165
+
Val: &tangled.RepoPull{
1166
+
Title: pull.Title,
1167
+
PullId: int64(pull.PullId),
1168
+
TargetRepo: string(f.RepoAt),
1169
+
TargetBranch: pull.TargetBranch,
1170
+
Patch: patch, // new patch
1171
+
Source: recordPullSource,
1176
1172
},
1177
-
})
1178
-
if err != nil {
1179
-
log.Println("failed to update record", err)
1180
-
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
1181
-
return
1182
-
}
1173
+
},
1174
+
})
1175
+
if err != nil {
1176
+
log.Println("failed to update record", err)
1177
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
1178
+
return
1179
+
}
1180
+
1181
+
if err = tx.Commit(); err != nil {
1182
+
log.Println("failed to commit transaction", err)
1183
+
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
1184
+
return
1185
+
}
1186
+
1187
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1188
+
return
1189
+
}
1190
+
1191
+
func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) {
1192
+
user := s.auth.GetUser(r)
1183
1193
1184
-
if err = tx.Commit(); err != nil {
1185
-
log.Println("failed to commit transaction", err)
1186
-
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
1187
-
return
1188
-
}
1194
+
pull, ok := r.Context().Value("pull").(*db.Pull)
1195
+
if !ok {
1196
+
log.Println("failed to get pull")
1197
+
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
1198
+
return
1199
+
}
1189
1200
1190
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1201
+
f, err := fullyResolvedRepo(r)
1202
+
if err != nil {
1203
+
log.Println("failed to get repo and knot", err)
1191
1204
return
1192
1205
}
1206
+
1207
+
if user.Did != pull.OwnerDid {
1208
+
log.Println("unauthorized user")
1209
+
w.WriteHeader(http.StatusUnauthorized)
1210
+
return
1211
+
}
1212
+
1213
+
forkRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String())
1214
+
if err != nil {
1215
+
log.Println("failed to get source repo", err)
1216
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1217
+
return
1218
+
}
1219
+
1220
+
// extract patch by performing compare
1221
+
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev)
1222
+
if err != nil {
1223
+
log.Printf("failed to create client for %s: %s", forkRepo.Knot, err)
1224
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1225
+
return
1226
+
}
1227
+
1228
+
secret, err := db.GetRegistrationKey(s.db, forkRepo.Knot)
1229
+
if err != nil {
1230
+
log.Printf("failed to get registration key for %s: %s", forkRepo.Knot, err)
1231
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1232
+
return
1233
+
}
1234
+
1235
+
// update the hidden tracking branch to latest
1236
+
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev)
1237
+
if err != nil {
1238
+
log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err)
1239
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1240
+
return
1241
+
}
1242
+
1243
+
resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch)
1244
+
if err != nil || resp.StatusCode != http.StatusNoContent {
1245
+
log.Printf("failed to update tracking branch: %s", err)
1246
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1247
+
return
1248
+
}
1249
+
1250
+
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch))
1251
+
diffTreeResponse, err := ksClient.Compare(forkRepo.Did, forkRepo.Name, hiddenRef, pull.PullSource.Branch)
1252
+
if err != nil {
1253
+
log.Printf("failed to compare branches: %s", err)
1254
+
s.pages.Notice(w, "resubmit-error", err.Error())
1255
+
return
1256
+
}
1257
+
1258
+
sourceRev := diffTreeResponse.DiffTree.Rev2
1259
+
patch := diffTreeResponse.DiffTree.Patch
1260
+
1261
+
if err = validateResubmittedPatch(pull, patch); err != nil {
1262
+
s.pages.Notice(w, "resubmit-error", err.Error())
1263
+
}
1264
+
1265
+
if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev {
1266
+
s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")
1267
+
return
1268
+
}
1269
+
1270
+
tx, err := s.db.BeginTx(r.Context(), nil)
1271
+
if err != nil {
1272
+
log.Println("failed to start tx")
1273
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1274
+
return
1275
+
}
1276
+
defer tx.Rollback()
1277
+
1278
+
err = db.ResubmitPull(tx, pull, patch, sourceRev)
1279
+
if err != nil {
1280
+
log.Println("failed to create pull request", err)
1281
+
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1282
+
return
1283
+
}
1284
+
client, _ := s.auth.AuthorizedClient(r)
1285
+
1286
+
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1287
+
if err != nil {
1288
+
// failed to get record
1289
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1290
+
return
1291
+
}
1292
+
1293
+
repoAt := pull.PullSource.RepoAt.String()
1294
+
recordPullSource := &tangled.RepoPull_Source{
1295
+
Branch: pull.PullSource.Branch,
1296
+
Repo: &repoAt,
1297
+
}
1298
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1299
+
Collection: tangled.RepoPullNSID,
1300
+
Repo: user.Did,
1301
+
Rkey: pull.Rkey,
1302
+
SwapRecord: ex.Cid,
1303
+
Record: &lexutil.LexiconTypeDecoder{
1304
+
Val: &tangled.RepoPull{
1305
+
Title: pull.Title,
1306
+
PullId: int64(pull.PullId),
1307
+
TargetRepo: string(f.RepoAt),
1308
+
TargetBranch: pull.TargetBranch,
1309
+
Patch: patch, // new patch
1310
+
Source: recordPullSource,
1311
+
},
1312
+
},
1313
+
})
1314
+
if err != nil {
1315
+
log.Println("failed to update record", err)
1316
+
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
1317
+
return
1318
+
}
1319
+
1320
+
if err = tx.Commit(); err != nil {
1321
+
log.Println("failed to commit transaction", err)
1322
+
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
1323
+
return
1324
+
}
1325
+
1326
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1327
+
return
1328
+
}
1329
+
1330
+
// validate a resubmission against a pull request
1331
+
func validateResubmittedPatch(pull *db.Pull, patch string) error {
1332
+
if patch == "" {
1333
+
return fmt.Errorf("Patch is empty.")
1334
+
}
1335
+
1336
+
if patch == pull.LatestPatch() {
1337
+
return fmt.Errorf("Patch is identical to previous submission.")
1338
+
}
1339
+
1340
+
if !isPatchValid(patch) {
1341
+
return fmt.Errorf("Invalid patch format. Please provide a valid diff.")
1342
+
}
1343
+
1344
+
return nil
1193
1345
}
1194
1346
1195
1347
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
+31
-3
appview/state/signer.go
+31
-3
appview/state/signer.go
···
7
7
"encoding/hex"
8
8
"encoding/json"
9
9
"fmt"
10
+
"io"
11
+
"log"
10
12
"net/http"
11
13
"net/url"
12
14
"time"
···
376
378
return &capabilities, nil
377
379
}
378
380
379
-
func (us *UnsignedClient) Compare(ownerDid, repoName, rev1, rev2 string) (*http.Response, error) {
381
+
func (us *UnsignedClient) Compare(ownerDid, repoName, rev1, rev2 string) (*types.RepoDiffTreeResponse, error) {
380
382
const (
381
383
Method = "GET"
382
384
)
···
385
387
386
388
req, err := us.newRequest(Method, endpoint, nil)
387
389
if err != nil {
388
-
return nil, err
390
+
return nil, fmt.Errorf("Failed to create request.")
389
391
}
390
392
391
-
return us.client.Do(req)
393
+
compareResp, err := us.client.Do(req)
394
+
if err != nil {
395
+
return nil, fmt.Errorf("Failed to create request.")
396
+
}
397
+
defer compareResp.Body.Close()
398
+
399
+
switch compareResp.StatusCode {
400
+
case 404:
401
+
case 400:
402
+
return nil, fmt.Errorf("Branch comparisons not supported on this knot.")
403
+
}
404
+
405
+
respBody, err := io.ReadAll(compareResp.Body)
406
+
if err != nil {
407
+
log.Println("failed to compare across branches")
408
+
return nil, fmt.Errorf("Failed to compare branches.")
409
+
}
410
+
defer compareResp.Body.Close()
411
+
412
+
var diffTreeResponse types.RepoDiffTreeResponse
413
+
err = json.Unmarshal(respBody, &diffTreeResponse)
414
+
if err != nil {
415
+
log.Println("failed to unmarshal diff tree response", err)
416
+
return nil, fmt.Errorf("Failed to compare branches.")
417
+
}
418
+
419
+
return &diffTreeResponse, nil
392
420
}
+1
-1
flake.nix
+1
-1
flake.nix
···
420
420
g = config.services.tangled-knotserver.gitUser;
421
421
in [
422
422
"d /var/lib/knotserver 0770 ${u} ${g} - -" # Create the directory first
423
-
"f+ /var/lib/knotserver/secret 0660 ${u} ${g} - KNOT_SERVER_SECRET=6995e040e80e2d593b5e5e9ca611a70140b9ef8044add0a28b48b1ee34aa3e85"
423
+
"f+ /var/lib/knotserver/secret 0660 ${u} ${g} - KNOT_SERVER_SECRET=5b42390da4c6659f34c9a545adebd8af82c4a19960d735f651e3d582623ba9f2"
424
424
];
425
425
services.tangled-knotserver = {
426
426
enable = true;