forked from tangled.org/core
this repo has no description

appview/pulls: improve ui + validate patches

Changed files
+98 -57
appview
pages
templates
repo
state
+1 -4
appview/pages/templates/repo/issues/issue.html
···
-
{{ define "title" }}
-
{{ .Issue.Title }} · issue #{{ .Issue.IssueId }} ·
-
{{ .RepoInfo.FullName }}
-
{{ end }}
+
{{ define "title" }}{{ .Issue.Title }} · issue #{{ .Issue.IssueId }} ·{{ .RepoInfo.FullName }}{{ end }}
{{ define "repoContent" }}
<header class="pb-4">
+15 -9
appview/pages/templates/repo/pulls/new.html
···
<p class="text-gray-500">
The branch you want to make your change against.
</p>
-
<select name="targetBranch" class="p-1 border border-gray-200 bg-white">
-
<option disabled selected>select a branch</option>
-
{{ range .Branches }}
-
<option
-
value="{{ .Reference.Name }}"
-
class="py-1">
-
{{ .Reference.Name }}
-
</option>
-
{{ end }}
+
<select
+
name="targetBranch"
+
class="p-1 border border-gray-200 bg-white"
+
>
+
<option disabled selected>select a branch</option>
+
{{ range .Branches }}
+
<option value="{{ .Reference.Name }}" class="py-1">
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
</select>
</div>
<div>
···
rows="10"
class="w-full resize-y font-mono"
placeholder="Paste your git-format-patch output here."
+
hx-post="/pulls/validate"
+
hx-trigger="input changed delay:500ms"
+
hx-target="#patch-validation"
+
hx-include="[name='patch']"
></textarea>
+
<div id="pull-validate"></div>
</div>
</div>
<div>
+29 -38
appview/pages/templates/repo/pulls/pull.html
···
{{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }}
{{ $isPushAllowed := .RepoInfo.Roles.IsPushAllowed }}
-
{{ if $isPullAuthor }}
+
{{ if and $isPullAuthor (not .Pull.State.IsMerged) }}
<section id="update-card" class="mt-8 space-y-4 relative">
{{ block "resubmitCard" . }} {{ end }}
</section>
···
{{ end }}
</section>
-
{{ if and (or $isPullAuthor $isPushAllowed) (not .Pull.State.IsMerged) }}
-
{{ $action := "close" }}
-
{{ $icon := "circle-x" }}
-
{{ $hoverColor := "red" }}
-
{{ if .Pull.State.IsClosed }}
-
{{ $action = "reopen" }}
-
{{ $icon = "circle-dot" }}
-
{{ $hoverColor = "green" }}
-
{{ end }}
-
<button
-
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/{{ $action }}"
-
hx-swap="none"
-
class="btn mt-8 text-sm flex items-center gap-2">
-
<i data-lucide="{{ $icon }}" class="w-4 h-4 mr-2 text-{{ $hoverColor }}-400"></i>
-
<span class="text-black">{{ $action }}</span>
-
</button>
-
{{ end }}
-
<div id="pull-close"></div>
<div id="pull-reopen"></div>
{{ end }}
···
<div
id="merge-status-card"
class="rounded relative bg-purple-50 border border-purple-200 p-4">
-
{{ if gt (len .Comments) 0 }}
-
<div
-
class="absolute left-8 -top-4 w-px h-4 bg-gray-300"
-
></div>
-
{{ else }}
-
<div
-
class="absolute left-8 -top-8 w-px h-8 bg-gray-300"
-
></div>
-
{{ end }}
-
<div class="flex items-center gap-2 text-purple-500">
<i data-lucide="git-merge" class="w-4 h-4"></i>
<span class="font-medium"
-
>Pull request successfully merged</span
+
>pull request successfully merged</span
>
</div>
···
{{ define "noConflictsCard" }}
{{ $isPushAllowed := .RepoInfo.Roles.IsPushAllowed }}
+
{{ $isPullAuthor := and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }}
<div
id="merge-status-card"
class="rounded relative border bg-green-50 border-green-200 p-4">
···
</button>
{{ end }}
+
{{ if and (or $isPullAuthor $isPushAllowed) (not .Pull.State.IsMerged) }}
+
{{ $action := "close" }}
+
{{ $icon := "circle-x" }}
+
{{ $hoverColor := "red" }}
+
{{ if .Pull.State.IsClosed }}
+
{{ $action = "reopen" }}
+
{{ $icon = "circle-dot" }}
+
{{ $hoverColor = "green" }}
+
{{ end }}
+
<button
+
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/{{ $action }}"
+
hx-swap="none"
+
class="btn mt-4 flex items-center gap-2">
+
<i data-lucide="{{ $icon }}" class="w-4 h-4 text-{{ $hoverColor }}-400"></i>
+
<span class="text-black">{{ $action }}</span>
+
</button>
+
{{ end }}
+
<div id="pull-merge-error" class="error"></div>
<div id="pull-merge-success" class="success"></div>
</div>
···
<div class="flex items-center gap-2 text-amber-500">
<i data-lucide="edit" class="w-4 h-4"></i>
-
<span class="font-medium">Resubmit your patch</span>
+
<span class="font-medium">resubmit your patch</span>
</div>
<div class="mt-2 text-sm text-gray-700">
-
You can update this patch to address reviews if any.
-
This begins a new round of reviews,
-
you can still view your previous submissions and reviews.
+
You can update this patch to address any reviews.
+
This will begin a new round of reviews,
+
but you'll still be able to view your previous submissions and feedback.
</div>
-
<div class="mt-4 flex items-center gap-2">
-
<form hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit" class="w-full">
+
<div class="mt-4 flex flex-col">
+
<form hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit" class="w-full" hx-swap="none">
<textarea
name="patch"
-
class="w-full p-2 rounded border border-gray-200"
-
placeholder="Enter new patch"
+
class="w-full p-2 mb-2 rounded border border-gray-200"
+
placeholder="Paste your updated patch here."
></textarea>
<button
type="submit"
+37 -1
appview/state/pull.go
···
"log"
"net/http"
"strconv"
+
"strings"
"time"
"github.com/sotangled/tangled/api/tangled"
···
return
}
+
// Validate patch format
+
if !isPatchValid(patch) {
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
+
return
+
}
+
tx, err := s.db.BeginTx(r.Context(), nil)
if err != nil {
log.Println("failed to start tx")
···
return
}
+
// Validate patch format
+
if !isPatchValid(patch) {
+
s.pages.Notice(w, "resubmit-error", "Invalid patch format. Please provide a valid diff.")
+
return
+
}
+
tx, err := s.db.BeginTx(r.Context(), nil)
if err != nil {
log.Println("failed to start tx")
···
}
// Merge the pull request
-
resp, err := ksClient.Merge([]byte(pull.LatestPatch()), user.Did, f.RepoName, pull.TargetBranch)
+
resp, err := ksClient.Merge([]byte(pull.LatestPatch()), user.Did, f.RepoName, pull.TargetBranch, pull.Title, pull.Body, "", "")
if err != nil {
log.Printf("failed to merge pull request: %s", err)
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
return
}
+
+
// Very basic validation to check if it looks like a diff/patch
+
// A valid patch usually starts with diff or --- lines
+
func isPatchValid(patch string) bool {
+
// Basic validation to check if it looks like a diff/patch
+
// A valid patch usually starts with diff or --- lines
+
if len(patch) == 0 {
+
return false
+
}
+
+
lines := strings.Split(patch, "\n")
+
if len(lines) < 2 {
+
return false
+
}
+
+
// Check for common patch format markers
+
firstLine := strings.TrimSpace(lines[0])
+
return strings.HasPrefix(firstLine, "diff ") ||
+
strings.HasPrefix(firstLine, "--- ") ||
+
strings.HasPrefix(firstLine, "Index: ") ||
+
strings.HasPrefix(firstLine, "+++ ") ||
+
strings.HasPrefix(firstLine, "@@ ")
+
}
+16 -5
appview/state/signer.go
···
"net/http"
"net/url"
"time"
+
+
"github.com/sotangled/tangled/types"
)
type SignerTransport struct {
···
return s.client.Do(req)
}
-
func (s *SignedClient) Merge(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) {
+
func (s *SignedClient) Merge(
+
patch []byte,
+
ownerDid, targetRepo, branch, commitMessage, commitBody, authorName, authorEmail string,
+
) (*http.Response, error) {
const (
Method = "POST"
)
endpoint := fmt.Sprintf("/%s/%s/merge", ownerDid, targetRepo)
+
+
mr := types.MergeRequest{
+
Branch: branch,
+
CommitMessage: commitMessage,
+
CommitBody: commitBody,
+
AuthorName: authorName,
+
AuthorEmail: authorEmail,
+
Patch: string(patch),
+
}
-
body, _ := json.Marshal(map[string]interface{}{
-
"patch": string(patch),
-
"branch": branch,
-
})
+
body, _ := json.Marshal(mr)
req, err := s.newRequest(Method, endpoint, body)
if err != nil {