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

appview: pulls: add compare across forks

anirudh.fi c2e3b7a7 d6b7f6e6

verified
+27 -7
appview/pages/pages.go
···
}
type RepoSinglePullParams struct {
-
LoggedInUser *auth.User
-
RepoInfo RepoInfo
-
Active string
-
DidHandleMap map[string]string
-
Pull *db.Pull
-
MergeCheck types.MergeCheckResponse
-
ResubmitCheck ResubmitResult
+
LoggedInUser *auth.User
+
RepoInfo RepoInfo
+
Active string
+
DidHandleMap map[string]string
+
Pull *db.Pull
+
PullSourceRepo *db.Repo
+
MergeCheck types.MergeCheckResponse
+
ResubmitCheck ResubmitResult
}
func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error {
···
func (p *Pages) PullCompareBranchesFragment(w io.Writer, params PullCompareBranchesParams) error {
return p.executePlain("fragments/pullCompareBranches", w, params)
+
}
+
+
type PullCompareForkParams struct {
+
RepoInfo RepoInfo
+
Forks []db.Repo
+
}
+
+
func (p *Pages) PullCompareForkFragment(w io.Writer, params PullCompareForkParams) error {
+
return p.executePlain("fragments/pullCompareForks", w, params)
+
}
+
+
type PullCompareForkBranchesParams struct {
+
RepoInfo RepoInfo
+
SourceBranches []types.Branch
+
TargetBranches []types.Branch
+
}
+
+
func (p *Pages) PullCompareForkBranchesFragment(w io.Writer, params PullCompareForkBranchesParams) error {
+
return p.executePlain("fragments/pullCompareForksBranches", w, params)
}
type PullResubmitParams struct {
+1 -12
appview/pages/templates/fragments/pullCompareBranches.html
···
{{ define "fragments/pullCompareBranches" }}
+
<div id="patch-upload">
<label for="targetBranch" class="dark:text-white">configure branches</label>
<div class="flex flex-wrap gap-2 items-center">
···
</option>
{{ end }}
</select>
-
-
<span class="text-sm">
-
... or upload a patch
-
<button
-
class="btn text-sm"
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload"
-
hx-swap="outerHTML"
-
hx-target="#patch-upload"
-
>
-
upload patch
-
</button>
-
</span>
</div>
</div>
+35
appview/pages/templates/fragments/pullCompareForks.html
···
+
{{ define "fragments/pullCompareForks" }}
+
<div id="patch-upload">
+
<label for="forkSelect" class="dark:text-white"
+
>select a fork to compare</label
+
>
+
<div class="flex flex-wrap gap-2 items-center mb-4">
+
<select
+
id="forkSelect"
+
name="fork"
+
required
+
class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+
hx-get="/{{ $.RepoInfo.FullName }}/pulls/new/fork-branches"
+
hx-target="#branch-selection"
+
hx-vals='{"fork": this.value}'
+
hx-swap="innerHTML"
+
onchange="document.getElementById('hiddenForkInput').value = this.value;"
+
>
+
<option disabled selected>select a fork</option>
+
{{ range .Forks }}
+
<option value="{{ .Name }}" class="py-1">
+
{{ .Name }}
+
</option>
+
{{ end }}
+
</select>
+
+
<input type="hidden" id="hiddenForkInput" name="fork" value="">
+
</div>
+
+
<div id="branch-selection" class="mt-2">
+
<div class="text-sm text-gray-500 dark:text-gray-400">
+
Select a fork first to view available branches
+
</div>
+
</div>
+
</div>
+
{{ end }}
+31
appview/pages/templates/fragments/pullCompareForksBranches.html
···
+
{{ define "fragments/pullCompareForksBranches" }}
+
<div class="flex flex-wrap gap-2 items-center">
+
<select
+
required
+
name="targetBranch"
+
class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+
>
+
<option disabled selected>target branch</option>
+
{{ range .TargetBranches }}
+
<option value="{{ .Reference.Name }}" class="py-1">
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</select>
+
+
{{ i "move-left" "w-5 h-5" }}
+
+
+
<select
+
name="sourceBranch"
+
class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+
>
+
<option disabled selected>source branch</option>
+
{{ range .SourceBranches }}
+
<option value="{{ .Reference.Name }}" class="py-1">
+
{{ .Reference.Name }}
+
</option>
+
{{ end }}
+
</select>
+
</div>
+
{{ end }}
+11 -23
appview/pages/templates/fragments/pullPatchUpload.html
···
{{ define "fragments/pullPatchUpload" }}
-
<div id="patch-upload">
-
<label for="patch" class="dark:text-white">paste your patch here</label>
-
<textarea
-
name="patch"
-
id="patch"
-
rows="10"
-
class="w-full resize-y font-mono dark:bg-gray-700 dark:text-white dark:border-gray-600"
-
placeholder="Paste your git diff output here."
-
></textarea>
-
-
{{ if .RepoInfo.Roles.IsPushAllowed }}
-
<div class="mt-4 text-sm">
-
You can also submit a pull request from a branch.
-
<button
-
class="btn text-sm"
-
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches"
-
hx-swap="outerHTML"
-
hx-target="#patch-upload"
-
>
-
compare branches
-
</button>
+
<div id="patch-upload">
+
<textarea
+
name="patch"
+
id="patch"
+
rows="12"
+
class="w-full resize-y font-mono dark:bg-gray-700 dark:text-white dark:border-gray-600"
+
placeholder="diff --git a/file.txt b/file.txt
+
index 1234567..abcdefg 100644
+
--- a/file.txt
+
+++ b/file.txt"
+
></textarea>
</div>
-
{{ end }}
-
</div>
{{ end }}
+41 -7
appview/pages/templates/repo/pulls/new.html
···
></textarea>
</div>
-
{{ if not .RepoInfo.Roles.IsPushAllowed }}
-
{{ template "fragments/pullPatchUpload" . }}
-
{{ else }}
-
{{ template "fragments/pullCompareBranches" . }}
-
{{ end }}
-
Or, select a fork to create a pull request from.
+
<label>choose your pull strategy</label>
+
<nav class="flex space-x-4">
+
<button
+
type="button"
+
class="px-3 py-2 pb-2 btn"
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload"
+
hx-target="#patch-strategy"
+
hx-swap="innerHTML"
+
>
+
paste patch
+
</button>
+
+
{{ if .RepoInfo.Roles.IsPushAllowed }}
+
<button
+
type="button"
+
class="px-3 py-2 pb-2 btn"
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches"
+
hx-target="#patch-strategy"
+
hx-swap="innerHTML"
+
>
+
compare branches
+
</button>
+
{{ end }}
+
<button
+
type="button"
+
class="px-3 py-2 pb-2 btn"
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-forks"
+
hx-target="#patch-strategy"
+
hx-swap="innerHTML"
+
>
+
compare forks
+
</button>
+
</nav>
+
+
<section id="patch-strategy">
+
{{ template "fragments/pullPatchUpload" . }}
+
</section>
<div class="flex justify-start items-center gap-2">
-
<button type="submit" class="btn">create pull</button>
+
<button type="submit" class="btn flex items-center gap-2">
+
{{ i "git-pull-request-create" "w-4 h-4" }}
+
create pull
+
</button>
</div>
</div>
+3 -6
appview/pages/templates/repo/pulls/pull.html
···
<span>
targeting
<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">
-
{{ .Pull.TargetBranch }}
+
<a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a>
</span>
</span>
{{ if not .Pull.IsPatch }}
<span>from
{{ if not .Pull.IsSameRepoBranch }}
-
<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">
-
{{ .Pull.PullSource.Repo }}
-
</span>
-
<span class="select-none">/</span>
+
<a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSourceRepo.Name }}</a>
{{ end }}
<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">
-
{{ .Pull.PullSource.Branch }}
+
<a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}/tree/{{ .Pull.PullSource.Branch }}" class="no-underline hover:underline">{{ .Pull.PullSource.Branch }}</a>
</span>
</span>
{{ end }}
+118 -7
appview/state/pull.go
···
resubmitResult = s.resubmitCheck(f, pull)
}
+
var pullSourceRepo *db.Repo
+
if pull.PullSource != nil {
+
if pull.PullSource.Repo != nil {
+
pullSourceRepo, err = db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String())
+
if err != nil {
+
log.Printf("failed to get repo by at uri: %v", err)
+
return
+
}
+
}
+
}
+
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
-
LoggedInUser: user,
-
RepoInfo: f.RepoInfo(s, user),
-
DidHandleMap: didHandleMap,
-
Pull: pull,
-
MergeCheck: mergeCheckResponse,
-
ResubmitCheck: resubmitResult,
+
LoggedInUser: user,
+
RepoInfo: f.RepoInfo(s, user),
+
DidHandleMap: didHandleMap,
+
Pull: pull,
+
PullSourceRepo: pullSourceRepo,
+
MergeCheck: mergeCheckResponse,
+
ResubmitCheck: resubmitResult,
})
}
···
title := r.FormValue("title")
body := r.FormValue("body")
targetBranch := r.FormValue("targetBranch")
-
fromFork := r.FormValue("fromFork")
+
fromFork := r.FormValue("fork")
sourceBranch := r.FormValue("sourceBranch")
patch := r.FormValue("patch")
···
s.pages.PullCompareBranchesFragment(w, pages.PullCompareBranchesParams{
RepoInfo: f.RepoInfo(s, user),
Branches: result.Branches,
+
})
+
}
+
+
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
+
user := s.auth.GetUser(r)
+
f, err := fullyResolvedRepo(r)
+
if err != nil {
+
log.Println("failed to get repo and knot", err)
+
return
+
}
+
+
forks, err := db.GetForksByDid(s.db, user.Did)
+
if err != nil {
+
log.Println("failed to get forks", err)
+
return
+
}
+
+
s.pages.PullCompareForkFragment(w, pages.PullCompareForkParams{
+
RepoInfo: f.RepoInfo(s, user),
+
Forks: forks,
+
})
+
}
+
+
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
+
user := s.auth.GetUser(r)
+
+
f, err := fullyResolvedRepo(r)
+
if err != nil {
+
log.Println("failed to get repo and knot", err)
+
return
+
}
+
+
forkVal := r.URL.Query().Get("fork")
+
+
// fork repo
+
repo, err := db.GetRepo(s.db, user.Did, forkVal)
+
if err != nil {
+
log.Println("failed to get repo", user.Did, forkVal)
+
return
+
}
+
+
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev)
+
if err != nil {
+
log.Printf("failed to create unsigned client for %s", repo.Knot)
+
s.pages.Error503(w)
+
return
+
}
+
+
sourceResp, err := sourceBranchesClient.Branches(user.Did, repo.Name)
+
if err != nil {
+
log.Println("failed to reach knotserver for source branches", err)
+
return
+
}
+
+
sourceBody, err := io.ReadAll(sourceResp.Body)
+
if err != nil {
+
log.Println("failed to read source response body", err)
+
return
+
}
+
defer sourceResp.Body.Close()
+
+
var sourceResult types.RepoBranchesResponse
+
err = json.Unmarshal(sourceBody, &sourceResult)
+
if err != nil {
+
log.Println("failed to parse source branches response:", err)
+
return
+
}
+
+
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
+
if err != nil {
+
log.Printf("failed to create unsigned client for target knot %s", f.Knot)
+
s.pages.Error503(w)
+
return
+
}
+
+
targetResp, err := targetBranchesClient.Branches(f.OwnerDid(), f.RepoName)
+
if err != nil {
+
log.Println("failed to reach knotserver for target branches", err)
+
return
+
}
+
+
targetBody, err := io.ReadAll(targetResp.Body)
+
if err != nil {
+
log.Println("failed to read target response body", err)
+
return
+
}
+
defer targetResp.Body.Close()
+
+
var targetResult types.RepoBranchesResponse
+
err = json.Unmarshal(targetBody, &targetResult)
+
if err != nil {
+
log.Println("failed to parse target branches response:", err)
+
return
+
}
+
+
s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{
+
RepoInfo: f.RepoInfo(s, user),
+
SourceBranches: sourceResult.Branches,
+
TargetBranches: targetResult.Branches,
})
}
+2
appview/state/router.go
···
r.Get("/", s.NewPull)
r.Get("/patch-upload", s.PatchUploadFragment)
r.Get("/compare-branches", s.CompareBranchesFragment)
+
r.Get("/compare-forks", s.CompareForksFragment)
+
r.Get("/fork-branches", s.CompareForksBranchesFragment)
r.Post("/", s.NewPull)
})