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

appview/models: move db.Pull* into models

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li dde635aa 9a4412df

verified
Changed files
+446 -436
appview
+7 -7
appview/db/profile.go
···
}
type PullEvents struct {
-
Items []*Pull
}
func (p PullEvents) Stats() PullEventStats {
var open, merged, closed int
for _, pull := range p.Items {
switch pull.State {
-
case PullOpen:
open += 1
-
case PullMerged:
merged += 1
-
case PullClosed:
closed += 1
}
}
···
switch stat {
case VanityStatMergedPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
-
args = append(args, did, PullMerged)
case VanityStatClosedPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
-
args = append(args, did, PullClosed)
case VanityStatOpenPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
-
args = append(args, did, PullOpen)
case VanityStatOpenIssueCount:
query = `select count(id) from issues where did = ? and open = 1`
args = append(args, did)
···
}
type PullEvents struct {
+
Items []*models.Pull
}
func (p PullEvents) Stats() PullEventStats {
var open, merged, closed int
for _, pull := range p.Items {
switch pull.State {
+
case models.PullOpen:
open += 1
+
case models.PullMerged:
merged += 1
+
case models.PullClosed:
closed += 1
}
}
···
switch stat {
case VanityStatMergedPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
+
args = append(args, did, models.PullMerged)
case VanityStatClosedPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
+
args = append(args, did, models.PullClosed)
case VanityStatOpenPRCount:
query = `select count(id) from pulls where owner_did = ? and state = ?`
+
args = append(args, did, models.PullOpen)
case VanityStatOpenIssueCount:
query = `select count(id) from issues where did = ? and open = 1`
args = append(args, did)
+42 -342
appview/db/pulls.go
···
"database/sql"
"fmt"
"log"
-
"slices"
"sort"
"strings"
"time"
"github.com/bluesky-social/indigo/atproto/syntax"
-
"tangled.org/core/api/tangled"
"tangled.org/core/appview/models"
-
"tangled.org/core/patchutil"
-
"tangled.org/core/types"
)
-
type PullState int
-
-
const (
-
PullClosed PullState = iota
-
PullOpen
-
PullMerged
-
PullDeleted
-
)
-
-
func (p PullState) String() string {
-
switch p {
-
case PullOpen:
-
return "open"
-
case PullMerged:
-
return "merged"
-
case PullClosed:
-
return "closed"
-
case PullDeleted:
-
return "deleted"
-
default:
-
return "closed"
-
}
-
}
-
-
func (p PullState) IsOpen() bool {
-
return p == PullOpen
-
}
-
func (p PullState) IsMerged() bool {
-
return p == PullMerged
-
}
-
func (p PullState) IsClosed() bool {
-
return p == PullClosed
-
}
-
func (p PullState) IsDeleted() bool {
-
return p == PullDeleted
-
}
-
-
type Pull struct {
-
// ids
-
ID int
-
PullId int
-
-
// at ids
-
RepoAt syntax.ATURI
-
OwnerDid string
-
Rkey string
-
-
// content
-
Title string
-
Body string
-
TargetBranch string
-
State PullState
-
Submissions []*PullSubmission
-
-
// stacking
-
StackId string // nullable string
-
ChangeId string // nullable string
-
ParentChangeId string // nullable string
-
-
// meta
-
Created time.Time
-
PullSource *PullSource
-
-
// optionally, populate this when querying for reverse mappings
-
Repo *models.Repo
-
}
-
-
func (p Pull) AsRecord() tangled.RepoPull {
-
var source *tangled.RepoPull_Source
-
if p.PullSource != nil {
-
s := p.PullSource.AsRecord()
-
source = &s
-
source.Sha = p.LatestSha()
-
}
-
-
record := tangled.RepoPull{
-
Title: p.Title,
-
Body: &p.Body,
-
CreatedAt: p.Created.Format(time.RFC3339),
-
Target: &tangled.RepoPull_Target{
-
Repo: p.RepoAt.String(),
-
Branch: p.TargetBranch,
-
},
-
Patch: p.LatestPatch(),
-
Source: source,
-
}
-
return record
-
}
-
-
type PullSource struct {
-
Branch string
-
RepoAt *syntax.ATURI
-
-
// optionally populate this for reverse mappings
-
Repo *models.Repo
-
}
-
-
func (p PullSource) AsRecord() tangled.RepoPull_Source {
-
var repoAt *string
-
if p.RepoAt != nil {
-
s := p.RepoAt.String()
-
repoAt = &s
-
}
-
record := tangled.RepoPull_Source{
-
Branch: p.Branch,
-
Repo: repoAt,
-
}
-
return record
-
}
-
-
type PullSubmission struct {
-
// ids
-
ID int
-
PullId int
-
-
// at ids
-
RepoAt syntax.ATURI
-
-
// content
-
RoundNumber int
-
Patch string
-
Comments []PullComment
-
SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs
-
-
// meta
-
Created time.Time
-
}
-
-
type PullComment struct {
-
// ids
-
ID int
-
PullId int
-
SubmissionId int
-
-
// at ids
-
RepoAt string
-
OwnerDid string
-
CommentAt string
-
-
// content
-
Body string
-
-
// meta
-
Created time.Time
-
}
-
-
func (p *Pull) LatestPatch() string {
-
latestSubmission := p.Submissions[p.LastRoundNumber()]
-
return latestSubmission.Patch
-
}
-
-
func (p *Pull) LatestSha() string {
-
latestSubmission := p.Submissions[p.LastRoundNumber()]
-
return latestSubmission.SourceRev
-
}
-
-
func (p *Pull) PullAt() syntax.ATURI {
-
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.OwnerDid, tangled.RepoPullNSID, p.Rkey))
-
}
-
-
func (p *Pull) LastRoundNumber() int {
-
return len(p.Submissions) - 1
-
}
-
-
func (p *Pull) IsPatchBased() bool {
-
return p.PullSource == nil
-
}
-
-
func (p *Pull) IsBranchBased() bool {
-
if p.PullSource != nil {
-
if p.PullSource.RepoAt != nil {
-
return p.PullSource.RepoAt == &p.RepoAt
-
} else {
-
// no repo specified
-
return true
-
}
-
}
-
return false
-
}
-
-
func (p *Pull) IsForkBased() bool {
-
if p.PullSource != nil {
-
if p.PullSource.RepoAt != nil {
-
// make sure repos are different
-
return p.PullSource.RepoAt != &p.RepoAt
-
}
-
}
-
return false
-
}
-
-
func (p *Pull) IsStacked() bool {
-
return p.StackId != ""
-
}
-
-
func (s PullSubmission) IsFormatPatch() bool {
-
return patchutil.IsFormatPatch(s.Patch)
-
}
-
-
func (s PullSubmission) AsFormatPatch() []types.FormatPatch {
-
patches, err := patchutil.ExtractPatches(s.Patch)
-
if err != nil {
-
log.Println("error extracting patches from submission:", err)
-
return []types.FormatPatch{}
-
}
-
-
return patches
-
}
-
-
func NewPull(tx *sql.Tx, pull *Pull) error {
_, err := tx.Exec(`
insert or ignore into repo_pull_seqs (repo_at, next_pull_id)
values (?, 1)
···
}
pull.PullId = nextId
-
pull.State = PullOpen
var sourceBranch, sourceRepoAt *string
if pull.PullSource != nil {
···
return pullId - 1, err
}
-
func GetPullsWithLimit(e Execer, limit int, filters ...filter) ([]*Pull, error) {
-
pulls := make(map[int]*Pull)
var conditions []string
var args []any
···
defer rows.Close()
for rows.Next() {
-
var pull Pull
var createdAt string
var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId sql.NullString
err := rows.Scan(
···
pull.Created = createdTime
if sourceBranch.Valid {
-
pull.PullSource = &PullSource{
Branch: sourceBranch.String,
}
if sourceRepoAt.Valid {
···
defer submissionsRows.Close()
for submissionsRows.Next() {
-
var s PullSubmission
var sourceRev sql.NullString
var createdAt string
err := submissionsRows.Scan(
···
}
if p, ok := pulls[s.PullId]; ok {
-
p.Submissions = make([]*PullSubmission, s.RoundNumber+1)
p.Submissions[s.RoundNumber] = &s
}
}
···
return nil, err
}
if p, ok := pulls[pullId]; ok {
-
p.Submissions[p.LastRoundNumber()].Comments = make([]PullComment, commentCount)
}
}
if err := rows.Err(); err != nil {
return nil, err
}
-
orderedByPullId := []*Pull{}
for _, p := range pulls {
orderedByPullId = append(orderedByPullId, p)
}
···
return orderedByPullId, nil
}
-
func GetPulls(e Execer, filters ...filter) ([]*Pull, error) {
return GetPullsWithLimit(e, 0, filters...)
}
-
func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, error) {
query := `
select
owner_did,
···
`
row := e.QueryRow(query, repoAt, pullId)
-
var pull Pull
var createdAt string
var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId sql.NullString
err := row.Scan(
···
// populate source
if sourceBranch.Valid {
-
pull.PullSource = &PullSource{
Branch: sourceBranch.String,
}
if sourceRepoAt.Valid {
···
}
defer submissionsRows.Close()
-
submissionsMap := make(map[int]*PullSubmission)
for submissionsRows.Next() {
-
var submission PullSubmission
var submissionCreatedStr string
var submissionSourceRev sql.NullString
err := submissionsRows.Scan(
···
defer commentsRows.Close()
for commentsRows.Next() {
-
var comment PullComment
var commentCreatedStr string
err := commentsRows.Scan(
&comment.ID,
···
}
}
-
pull.Submissions = make([]*PullSubmission, len(submissionsMap))
for _, submission := range submissionsMap {
pull.Submissions[submission.RoundNumber] = submission
}
···
// timeframe here is directly passed into the sql query filter, and any
// timeframe in the past should be negative; e.g.: "-3 months"
-
func GetPullsByOwnerDid(e Execer, did, timeframe string) ([]Pull, error) {
-
var pulls []Pull
rows, err := e.Query(`
select
···
defer rows.Close()
for rows.Next() {
-
var pull Pull
var repo models.Repo
var pullCreatedAt, repoCreatedAt string
err := rows.Scan(
···
return pulls, nil
}
-
func NewPullComment(e Execer, comment *PullComment) (int64, error) {
query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)`
res, err := e.Exec(
query,
···
return i, nil
}
-
func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState PullState) error {
_, err := e.Exec(
`update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`,
pullState,
repoAt,
pullId,
-
PullDeleted, // only update state of non-deleted pulls
-
PullMerged, // only update state of non-merged pulls
)
return err
}
func ClosePull(e Execer, repoAt syntax.ATURI, pullId int) error {
-
err := SetPullState(e, repoAt, pullId, PullClosed)
return err
}
func ReopenPull(e Execer, repoAt syntax.ATURI, pullId int) error {
-
err := SetPullState(e, repoAt, pullId, PullOpen)
return err
}
func MergePull(e Execer, repoAt syntax.ATURI, pullId int) error {
-
err := SetPullState(e, repoAt, pullId, PullMerged)
return err
}
func DeletePull(e Execer, repoAt syntax.ATURI, pullId int) error {
-
err := SetPullState(e, repoAt, pullId, PullDeleted)
return err
}
-
func ResubmitPull(e Execer, pull *Pull, newPatch, sourceRev string) error {
newRoundNumber := len(pull.Submissions)
_, err := e.Exec(`
insert into pull_submissions (pull_id, repo_at, round_number, patch, source_rev)
···
count(case when state = ? then 1 end) as deleted_count
from pulls
where repo_at = ?`,
-
PullOpen,
-
PullMerged,
-
PullClosed,
-
PullDeleted,
repoAt,
)
···
return count, nil
}
-
type Stack []*Pull
-
// change-id parent-change-id
//
// 4 w ,-------- z (TOP)
···
// 1 x <------' nil (BOT)
//
// `w` is parent of none, so it is the top of the stack
-
func GetStack(e Execer, stackId string) (Stack, error) {
unorderedPulls, err := GetPulls(
e,
FilterEq("stack_id", stackId),
-
FilterNotEq("state", PullDeleted),
)
if err != nil {
return nil, err
}
// map of parent-change-id to pull
-
changeIdMap := make(map[string]*Pull, len(unorderedPulls))
-
parentMap := make(map[string]*Pull, len(unorderedPulls))
for _, p := range unorderedPulls {
changeIdMap[p.ChangeId] = p
if p.ParentChangeId != "" {
···
}
// the top of the stack is the pull that is not a parent of any pull
-
var topPull *Pull
for _, maybeTop := range unorderedPulls {
if _, ok := parentMap[maybeTop.ChangeId]; !ok {
topPull = maybeTop
···
}
}
-
pulls := []*Pull{}
for {
pulls = append(pulls, topPull)
if topPull.ParentChangeId != "" {
···
return pulls, nil
}
-
func GetAbandonedPulls(e Execer, stackId string) ([]*Pull, error) {
pulls, err := GetPulls(
e,
FilterEq("stack_id", stackId),
-
FilterEq("state", PullDeleted),
)
if err != nil {
return nil, err
···
return pulls, nil
}
-
-
// position of this pull in the stack
-
func (stack Stack) Position(pull *Pull) int {
-
return slices.IndexFunc(stack, func(p *Pull) bool {
-
return p.ChangeId == pull.ChangeId
-
})
-
}
-
-
// all pulls below this pull (including self) in this stack
-
//
-
// nil if this pull does not belong to this stack
-
func (stack Stack) Below(pull *Pull) Stack {
-
position := stack.Position(pull)
-
-
if position < 0 {
-
return nil
-
}
-
-
return stack[position:]
-
}
-
-
// all pulls below this pull (excluding self) in this stack
-
func (stack Stack) StrictlyBelow(pull *Pull) Stack {
-
below := stack.Below(pull)
-
-
if len(below) > 0 {
-
return below[1:]
-
}
-
-
return nil
-
}
-
-
// all pulls above this pull (including self) in this stack
-
func (stack Stack) Above(pull *Pull) Stack {
-
position := stack.Position(pull)
-
-
if position < 0 {
-
return nil
-
}
-
-
return stack[:position+1]
-
}
-
-
// all pulls below this pull (excluding self) in this stack
-
func (stack Stack) StrictlyAbove(pull *Pull) Stack {
-
above := stack.Above(pull)
-
-
if len(above) > 0 {
-
return above[:len(above)-1]
-
}
-
-
return nil
-
}
-
-
// the combined format-patches of all the newest submissions in this stack
-
func (stack Stack) CombinedPatch() string {
-
// go in reverse order because the bottom of the stack is the last element in the slice
-
var combined strings.Builder
-
for idx := range stack {
-
pull := stack[len(stack)-1-idx]
-
combined.WriteString(pull.LatestPatch())
-
combined.WriteString("\n")
-
}
-
return combined.String()
-
}
-
-
// filter out PRs that are "active"
-
//
-
// PRs that are still open are active
-
func (stack Stack) Mergeable() Stack {
-
var mergeable Stack
-
-
for _, p := range stack {
-
// stop at the first merged PR
-
if p.State == PullMerged || p.State == PullClosed {
-
break
-
}
-
-
// skip over deleted PRs
-
if p.State != PullDeleted {
-
mergeable = append(mergeable, p)
-
}
-
}
-
-
return mergeable
-
}
···
"database/sql"
"fmt"
"log"
"sort"
"strings"
"time"
"github.com/bluesky-social/indigo/atproto/syntax"
"tangled.org/core/appview/models"
)
+
func NewPull(tx *sql.Tx, pull *models.Pull) error {
_, err := tx.Exec(`
insert or ignore into repo_pull_seqs (repo_at, next_pull_id)
values (?, 1)
···
}
pull.PullId = nextId
+
pull.State = models.PullOpen
var sourceBranch, sourceRepoAt *string
if pull.PullSource != nil {
···
return pullId - 1, err
}
+
func GetPullsWithLimit(e Execer, limit int, filters ...filter) ([]*models.Pull, error) {
+
pulls := make(map[int]*models.Pull)
var conditions []string
var args []any
···
defer rows.Close()
for rows.Next() {
+
var pull models.Pull
var createdAt string
var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId sql.NullString
err := rows.Scan(
···
pull.Created = createdTime
if sourceBranch.Valid {
+
pull.PullSource = &models.PullSource{
Branch: sourceBranch.String,
}
if sourceRepoAt.Valid {
···
defer submissionsRows.Close()
for submissionsRows.Next() {
+
var s models.PullSubmission
var sourceRev sql.NullString
var createdAt string
err := submissionsRows.Scan(
···
}
if p, ok := pulls[s.PullId]; ok {
+
p.Submissions = make([]*models.PullSubmission, s.RoundNumber+1)
p.Submissions[s.RoundNumber] = &s
}
}
···
return nil, err
}
if p, ok := pulls[pullId]; ok {
+
p.Submissions[p.LastRoundNumber()].Comments = make([]models.PullComment, commentCount)
}
}
if err := rows.Err(); err != nil {
return nil, err
}
+
orderedByPullId := []*models.Pull{}
for _, p := range pulls {
orderedByPullId = append(orderedByPullId, p)
}
···
return orderedByPullId, nil
}
+
func GetPulls(e Execer, filters ...filter) ([]*models.Pull, error) {
return GetPullsWithLimit(e, 0, filters...)
}
+
func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*models.Pull, error) {
query := `
select
owner_did,
···
`
row := e.QueryRow(query, repoAt, pullId)
+
var pull models.Pull
var createdAt string
var sourceBranch, sourceRepoAt, stackId, changeId, parentChangeId sql.NullString
err := row.Scan(
···
// populate source
if sourceBranch.Valid {
+
pull.PullSource = &models.PullSource{
Branch: sourceBranch.String,
}
if sourceRepoAt.Valid {
···
}
defer submissionsRows.Close()
+
submissionsMap := make(map[int]*models.PullSubmission)
for submissionsRows.Next() {
+
var submission models.PullSubmission
var submissionCreatedStr string
var submissionSourceRev sql.NullString
err := submissionsRows.Scan(
···
defer commentsRows.Close()
for commentsRows.Next() {
+
var comment models.PullComment
var commentCreatedStr string
err := commentsRows.Scan(
&comment.ID,
···
}
}
+
pull.Submissions = make([]*models.PullSubmission, len(submissionsMap))
for _, submission := range submissionsMap {
pull.Submissions[submission.RoundNumber] = submission
}
···
// timeframe here is directly passed into the sql query filter, and any
// timeframe in the past should be negative; e.g.: "-3 months"
+
func GetPullsByOwnerDid(e Execer, did, timeframe string) ([]models.Pull, error) {
+
var pulls []models.Pull
rows, err := e.Query(`
select
···
defer rows.Close()
for rows.Next() {
+
var pull models.Pull
var repo models.Repo
var pullCreatedAt, repoCreatedAt string
err := rows.Scan(
···
return pulls, nil
}
+
func NewPullComment(e Execer, comment *models.PullComment) (int64, error) {
query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)`
res, err := e.Exec(
query,
···
return i, nil
}
+
func SetPullState(e Execer, repoAt syntax.ATURI, pullId int, pullState models.PullState) error {
_, err := e.Exec(
`update pulls set state = ? where repo_at = ? and pull_id = ? and (state <> ? or state <> ?)`,
pullState,
repoAt,
pullId,
+
models.PullDeleted, // only update state of non-deleted pulls
+
models.PullMerged, // only update state of non-merged pulls
)
return err
}
func ClosePull(e Execer, repoAt syntax.ATURI, pullId int) error {
+
err := SetPullState(e, repoAt, pullId, models.PullClosed)
return err
}
func ReopenPull(e Execer, repoAt syntax.ATURI, pullId int) error {
+
err := SetPullState(e, repoAt, pullId, models.PullOpen)
return err
}
func MergePull(e Execer, repoAt syntax.ATURI, pullId int) error {
+
err := SetPullState(e, repoAt, pullId, models.PullMerged)
return err
}
func DeletePull(e Execer, repoAt syntax.ATURI, pullId int) error {
+
err := SetPullState(e, repoAt, pullId, models.PullDeleted)
return err
}
+
func ResubmitPull(e Execer, pull *models.Pull, newPatch, sourceRev string) error {
newRoundNumber := len(pull.Submissions)
_, err := e.Exec(`
insert into pull_submissions (pull_id, repo_at, round_number, patch, source_rev)
···
count(case when state = ? then 1 end) as deleted_count
from pulls
where repo_at = ?`,
+
models.PullOpen,
+
models.PullMerged,
+
models.PullClosed,
+
models.PullDeleted,
repoAt,
)
···
return count, nil
}
// change-id parent-change-id
//
// 4 w ,-------- z (TOP)
···
// 1 x <------' nil (BOT)
//
// `w` is parent of none, so it is the top of the stack
+
func GetStack(e Execer, stackId string) (models.Stack, error) {
unorderedPulls, err := GetPulls(
e,
FilterEq("stack_id", stackId),
+
FilterNotEq("state", models.PullDeleted),
)
if err != nil {
return nil, err
}
// map of parent-change-id to pull
+
changeIdMap := make(map[string]*models.Pull, len(unorderedPulls))
+
parentMap := make(map[string]*models.Pull, len(unorderedPulls))
for _, p := range unorderedPulls {
changeIdMap[p.ChangeId] = p
if p.ParentChangeId != "" {
···
}
// the top of the stack is the pull that is not a parent of any pull
+
var topPull *models.Pull
for _, maybeTop := range unorderedPulls {
if _, ok := parentMap[maybeTop.ChangeId]; !ok {
topPull = maybeTop
···
}
}
+
pulls := []*models.Pull{}
for {
pulls = append(pulls, topPull)
if topPull.ParentChangeId != "" {
···
return pulls, nil
}
+
func GetAbandonedPulls(e Execer, stackId string) ([]*models.Pull, error) {
pulls, err := GetPulls(
e,
FilterEq("stack_id", stackId),
+
FilterEq("state", models.PullDeleted),
)
if err != nil {
return nil, err
···
return pulls, nil
}
+4 -4
appview/db/repos.go
···
inClause,
)
args = append([]any{
-
PullOpen,
-
PullMerged,
-
PullClosed,
-
PullDeleted,
}, args...)
rows, err = e.Query(
pullCountQuery,
···
inClause,
)
args = append([]any{
+
models.PullOpen,
+
models.PullMerged,
+
models.PullClosed,
+
models.PullDeleted,
}, args...)
rows, err = e.Query(
pullCountQuery,
+310
appview/models/pull.go
···
···
+
package models
+
+
import (
+
"fmt"
+
"log"
+
"slices"
+
"strings"
+
"time"
+
+
"github.com/bluesky-social/indigo/atproto/syntax"
+
"tangled.org/core/api/tangled"
+
"tangled.org/core/patchutil"
+
"tangled.org/core/types"
+
)
+
+
type PullState int
+
+
const (
+
PullClosed PullState = iota
+
PullOpen
+
PullMerged
+
PullDeleted
+
)
+
+
func (p PullState) String() string {
+
switch p {
+
case PullOpen:
+
return "open"
+
case PullMerged:
+
return "merged"
+
case PullClosed:
+
return "closed"
+
case PullDeleted:
+
return "deleted"
+
default:
+
return "closed"
+
}
+
}
+
+
func (p PullState) IsOpen() bool {
+
return p == PullOpen
+
}
+
func (p PullState) IsMerged() bool {
+
return p == PullMerged
+
}
+
func (p PullState) IsClosed() bool {
+
return p == PullClosed
+
}
+
func (p PullState) IsDeleted() bool {
+
return p == PullDeleted
+
}
+
+
type Pull struct {
+
// ids
+
ID int
+
PullId int
+
+
// at ids
+
RepoAt syntax.ATURI
+
OwnerDid string
+
Rkey string
+
+
// content
+
Title string
+
Body string
+
TargetBranch string
+
State PullState
+
Submissions []*PullSubmission
+
+
// stacking
+
StackId string // nullable string
+
ChangeId string // nullable string
+
ParentChangeId string // nullable string
+
+
// meta
+
Created time.Time
+
PullSource *PullSource
+
+
// optionally, populate this when querying for reverse mappings
+
Repo *Repo
+
}
+
+
func (p Pull) AsRecord() tangled.RepoPull {
+
var source *tangled.RepoPull_Source
+
if p.PullSource != nil {
+
s := p.PullSource.AsRecord()
+
source = &s
+
source.Sha = p.LatestSha()
+
}
+
+
record := tangled.RepoPull{
+
Title: p.Title,
+
Body: &p.Body,
+
CreatedAt: p.Created.Format(time.RFC3339),
+
Target: &tangled.RepoPull_Target{
+
Repo: p.RepoAt.String(),
+
Branch: p.TargetBranch,
+
},
+
Patch: p.LatestPatch(),
+
Source: source,
+
}
+
return record
+
}
+
+
type PullSource struct {
+
Branch string
+
RepoAt *syntax.ATURI
+
+
// optionally populate this for reverse mappings
+
Repo *Repo
+
}
+
+
func (p PullSource) AsRecord() tangled.RepoPull_Source {
+
var repoAt *string
+
if p.RepoAt != nil {
+
s := p.RepoAt.String()
+
repoAt = &s
+
}
+
record := tangled.RepoPull_Source{
+
Branch: p.Branch,
+
Repo: repoAt,
+
}
+
return record
+
}
+
+
type PullSubmission struct {
+
// ids
+
ID int
+
PullId int
+
+
// at ids
+
RepoAt syntax.ATURI
+
+
// content
+
RoundNumber int
+
Patch string
+
Comments []PullComment
+
SourceRev string // include the rev that was used to create this submission: only for branch/fork PRs
+
+
// meta
+
Created time.Time
+
}
+
+
type PullComment struct {
+
// ids
+
ID int
+
PullId int
+
SubmissionId int
+
+
// at ids
+
RepoAt string
+
OwnerDid string
+
CommentAt string
+
+
// content
+
Body string
+
+
// meta
+
Created time.Time
+
}
+
+
func (p *Pull) LatestPatch() string {
+
latestSubmission := p.Submissions[p.LastRoundNumber()]
+
return latestSubmission.Patch
+
}
+
+
func (p *Pull) LatestSha() string {
+
latestSubmission := p.Submissions[p.LastRoundNumber()]
+
return latestSubmission.SourceRev
+
}
+
+
func (p *Pull) PullAt() syntax.ATURI {
+
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", p.OwnerDid, tangled.RepoPullNSID, p.Rkey))
+
}
+
+
func (p *Pull) LastRoundNumber() int {
+
return len(p.Submissions) - 1
+
}
+
+
func (p *Pull) IsPatchBased() bool {
+
return p.PullSource == nil
+
}
+
+
func (p *Pull) IsBranchBased() bool {
+
if p.PullSource != nil {
+
if p.PullSource.RepoAt != nil {
+
return p.PullSource.RepoAt == &p.RepoAt
+
} else {
+
// no repo specified
+
return true
+
}
+
}
+
return false
+
}
+
+
func (p *Pull) IsForkBased() bool {
+
if p.PullSource != nil {
+
if p.PullSource.RepoAt != nil {
+
// make sure repos are different
+
return p.PullSource.RepoAt != &p.RepoAt
+
}
+
}
+
return false
+
}
+
+
func (p *Pull) IsStacked() bool {
+
return p.StackId != ""
+
}
+
+
func (s PullSubmission) IsFormatPatch() bool {
+
return patchutil.IsFormatPatch(s.Patch)
+
}
+
+
func (s PullSubmission) AsFormatPatch() []types.FormatPatch {
+
patches, err := patchutil.ExtractPatches(s.Patch)
+
if err != nil {
+
log.Println("error extracting patches from submission:", err)
+
return []types.FormatPatch{}
+
}
+
+
return patches
+
}
+
+
type Stack []*Pull
+
+
// position of this pull in the stack
+
func (stack Stack) Position(pull *Pull) int {
+
return slices.IndexFunc(stack, func(p *Pull) bool {
+
return p.ChangeId == pull.ChangeId
+
})
+
}
+
+
// all pulls below this pull (including self) in this stack
+
//
+
// nil if this pull does not belong to this stack
+
func (stack Stack) Below(pull *Pull) Stack {
+
position := stack.Position(pull)
+
+
if position < 0 {
+
return nil
+
}
+
+
return stack[position:]
+
}
+
+
// all pulls below this pull (excluding self) in this stack
+
func (stack Stack) StrictlyBelow(pull *Pull) Stack {
+
below := stack.Below(pull)
+
+
if len(below) > 0 {
+
return below[1:]
+
}
+
+
return nil
+
}
+
+
// all pulls above this pull (including self) in this stack
+
func (stack Stack) Above(pull *Pull) Stack {
+
position := stack.Position(pull)
+
+
if position < 0 {
+
return nil
+
}
+
+
return stack[:position+1]
+
}
+
+
// all pulls below this pull (excluding self) in this stack
+
func (stack Stack) StrictlyAbove(pull *Pull) Stack {
+
above := stack.Above(pull)
+
+
if len(above) > 0 {
+
return above[:len(above)-1]
+
}
+
+
return nil
+
}
+
+
// the combined format-patches of all the newest submissions in this stack
+
func (stack Stack) CombinedPatch() string {
+
// go in reverse order because the bottom of the stack is the last element in the slice
+
var combined strings.Builder
+
for idx := range stack {
+
pull := stack[len(stack)-1-idx]
+
combined.WriteString(pull.LatestPatch())
+
combined.WriteString("\n")
+
}
+
return combined.String()
+
}
+
+
// filter out PRs that are "active"
+
//
+
// PRs that are still open are active
+
func (stack Stack) Mergeable() Stack {
+
var mergeable Stack
+
+
for _, p := range stack {
+
// stop at the first merged PR
+
if p.State == PullMerged || p.State == PullClosed {
+
break
+
}
+
+
// skip over deleted PRs
+
if p.State != PullDeleted {
+
mergeable = append(mergeable, p)
+
}
+
}
+
+
return mergeable
+
}
+2 -2
appview/notify/merged_notifier.go
···
}
}
-
func (m *mergedNotifier) NewPull(ctx context.Context, pull *db.Pull) {
for _, notifier := range m.notifiers {
notifier.NewPull(ctx, pull)
}
}
-
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {
for _, notifier := range m.notifiers {
notifier.NewPullComment(ctx, comment)
}
···
}
}
+
func (m *mergedNotifier) NewPull(ctx context.Context, pull *models.Pull) {
for _, notifier := range m.notifiers {
notifier.NewPull(ctx, pull)
}
}
+
func (m *mergedNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) {
for _, notifier := range m.notifiers {
notifier.NewPullComment(ctx, comment)
}
+4 -4
appview/notify/notifier.go
···
NewFollow(ctx context.Context, follow *models.Follow)
DeleteFollow(ctx context.Context, follow *models.Follow)
-
NewPull(ctx context.Context, pull *db.Pull)
-
NewPullComment(ctx context.Context, comment *db.PullComment)
UpdateProfile(ctx context.Context, profile *db.Profile)
···
func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {}
func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {}
-
func (m *BaseNotifier) NewPull(ctx context.Context, pull *db.Pull) {}
-
func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {}
func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) {}
···
NewFollow(ctx context.Context, follow *models.Follow)
DeleteFollow(ctx context.Context, follow *models.Follow)
+
NewPull(ctx context.Context, pull *models.Pull)
+
NewPullComment(ctx context.Context, comment *models.PullComment)
UpdateProfile(ctx context.Context, profile *db.Profile)
···
func (m *BaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {}
func (m *BaseNotifier) DeleteFollow(ctx context.Context, follow *models.Follow) {}
+
func (m *BaseNotifier) NewPull(ctx context.Context, pull *models.Pull) {}
+
func (m *BaseNotifier) NewPullComment(ctx context.Context, models *models.PullComment) {}
func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) {}
+14 -14
appview/pages/pages.go
···
type RepoPullsParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pulls []*db.Pull
Active string
-
FilteringBy db.PullState
-
Stacks map[string]db.Stack
Pipelines map[string]db.Pipeline
}
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
-
Pull *db.Pull
-
Stack db.Stack
-
AbandonedPulls []*db.Pull
MergeCheck types.MergeCheckResponse
ResubmitCheck ResubmitResult
Pipelines map[string]db.Pipeline
···
type RepoPullPatchParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pull *db.Pull
-
Stack db.Stack
Diff *types.NiceDiff
Round int
-
Submission *db.PullSubmission
OrderedReactionKinds []db.ReactionKind
DiffOpts types.DiffOpts
}
···
type RepoPullInterdiffParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pull *db.Pull
Round int
Interdiff *patchutil.InterdiffResult
OrderedReactionKinds []db.ReactionKind
···
type PullResubmitParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pull *db.Pull
SubmissionId int
}
···
type PullActionsParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pull *db.Pull
RoundNumber int
MergeCheck types.MergeCheckResponse
ResubmitCheck ResubmitResult
-
Stack db.Stack
}
func (p *Pages) PullActionsFragment(w io.Writer, params PullActionsParams) error {
···
type PullNewCommentParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
-
Pull *db.Pull
RoundNumber int
}
···
type RepoPullsParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pulls []*models.Pull
Active string
+
FilteringBy models.PullState
+
Stacks map[string]models.Stack
Pipelines map[string]db.Pipeline
}
···
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
Active string
+
Pull *models.Pull
+
Stack models.Stack
+
AbandonedPulls []*models.Pull
MergeCheck types.MergeCheckResponse
ResubmitCheck ResubmitResult
Pipelines map[string]db.Pipeline
···
type RepoPullPatchParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pull *models.Pull
+
Stack models.Stack
Diff *types.NiceDiff
Round int
+
Submission *models.PullSubmission
OrderedReactionKinds []db.ReactionKind
DiffOpts types.DiffOpts
}
···
type RepoPullInterdiffParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pull *models.Pull
Round int
Interdiff *patchutil.InterdiffResult
OrderedReactionKinds []db.ReactionKind
···
type PullResubmitParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pull *models.Pull
SubmissionId int
}
···
type PullActionsParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pull *models.Pull
RoundNumber int
MergeCheck types.MergeCheckResponse
ResubmitCheck ResubmitResult
+
Stack models.Stack
}
func (p *Pages) PullActionsFragment(w io.Writer, params PullActionsParams) error {
···
type PullNewCommentParams struct {
LoggedInUser *oauth.User
RepoInfo repoinfo.RepoInfo
+
Pull *models.Pull
RoundNumber int
}
+2 -2
appview/posthog/notifier.go
···
}
}
-
func (n *posthogNotifier) NewPull(ctx context.Context, pull *db.Pull) {
err := n.client.Enqueue(posthog.Capture{
DistinctId: pull.OwnerDid,
Event: "new_pull",
···
}
}
-
func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {
err := n.client.Enqueue(posthog.Capture{
DistinctId: comment.OwnerDid,
Event: "new_pull_comment",
···
}
}
+
func (n *posthogNotifier) NewPull(ctx context.Context, pull *models.Pull) {
err := n.client.Enqueue(posthog.Capture{
DistinctId: pull.OwnerDid,
Event: "new_pull",
···
}
}
+
func (n *posthogNotifier) NewPullComment(ctx context.Context, comment *models.PullComment) {
err := n.client.Enqueue(posthog.Capture{
DistinctId: comment.OwnerDid,
Event: "new_pull_comment",
+54 -54
appview/pulls/pulls.go
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
// can be nil if this pull is not stacked
-
stack, _ := r.Context().Value("stack").(db.Stack)
roundNumberStr := chi.URLParam(r, "round")
roundNumber, err := strconv.Atoi(roundNumberStr)
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
// can be nil if this pull is not stacked
-
stack, _ := r.Context().Value("stack").(db.Stack)
-
abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*db.Pull)
totalIdents := 1
for _, submission := range pull.Submissions {
···
})
}
-
func (s *Pulls) mergeCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) types.MergeCheckResponse {
-
if pull.State == db.PullMerged {
return types.MergeCheckResponse{}
}
···
return result
}
-
func (s *Pulls) resubmitCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult {
-
if pull.State == db.PullMerged || pull.State == db.PullDeleted || pull.PullSource == nil {
return pages.Unknown
}
···
diffOpts.Split = true
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
return
}
-
stack, _ := r.Context().Value("stack").(db.Stack)
roundId := chi.URLParam(r, "round")
roundIdInt, err := strconv.Atoi(roundId)
···
diffOpts.Split = true
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to get pull.")
···
}
func (s *Pulls) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) {
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
user := s.oauth.GetUser(r)
params := r.URL.Query()
-
state := db.PullOpen
switch params.Get("state") {
case "closed":
-
state = db.PullClosed
case "merged":
-
state = db.PullMerged
}
f, err := s.repoResolver.Resolve(r)
···
}
// we want to group all stacked PRs into just one list
-
stacks := make(map[string]db.Stack)
var shas []string
n := 0
for _, p := range pulls {
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
return
}
-
comment := &db.PullComment{
OwnerDid: user.Did,
RepoAt: f.RepoAt().String(),
PullId: pull.PullId,
···
return
}
-
pullSource := &db.PullSource{
Branch: sourceBranch,
}
recordPullSource := &tangled.RepoPull_Source{
···
forkAtUri := fork.RepoAt()
forkAtUriStr := forkAtUri.String()
-
pullSource := &db.PullSource{
Branch: sourceBranch,
RepoAt: &forkAtUri,
}
···
title, body, targetBranch string,
patch string,
sourceRev string,
-
pullSource *db.PullSource,
recordPullSource *tangled.RepoPull_Source,
isStacked bool,
) {
···
}
rkey := tid.TID()
-
initialSubmission := db.PullSubmission{
Patch: patch,
SourceRev: sourceRev,
}
-
pull := &db.Pull{
Title: title,
Body: body,
TargetBranch: targetBranch,
OwnerDid: user.Did,
RepoAt: f.RepoAt(),
Rkey: rkey,
-
Submissions: []*db.PullSubmission{
&initialSubmission,
},
PullSource: pullSource,
···
targetBranch string,
patch string,
sourceRev string,
-
pullSource *db.PullSource,
) {
// run some necessary checks for stacked-prs first
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitPatch(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
···
}
// validate a resubmission against a pull request
-
func validateResubmittedPatch(pull *db.Pull, patch string) error {
if patch == "" {
return fmt.Errorf("Patch is empty.")
}
···
r *http.Request,
f *reporesolver.ResolvedRepo,
user *oauth.User,
-
pull *db.Pull,
patch string,
sourceRev string,
) {
···
r *http.Request,
f *reporesolver.ResolvedRepo,
user *oauth.User,
-
pull *db.Pull,
patch string,
stackId string,
) {
targetBranch := pull.TargetBranch
-
origStack, _ := r.Context().Value("stack").(db.Stack)
newStack, err := newStack(f, user, targetBranch, patch, pull.PullSource, stackId)
if err != nil {
log.Println("failed to create resubmitted stack", err)
···
}
// find the diff between the stacks, first, map them by changeId
-
origById := make(map[string]*db.Pull)
-
newById := make(map[string]*db.Pull)
for _, p := range origStack {
origById[p.ChangeId] = p
}
···
// commits that got updated: corresponding pull is resubmitted & new round begins
//
// for commits that were unchanged: no changes, parent-change-id is updated as necessary
-
additions := make(map[string]*db.Pull)
-
deletions := make(map[string]*db.Pull)
unchanged := make(map[string]struct{})
updated := make(map[string]struct{})
···
// deleted pulls are marked as deleted in the DB
for _, p := range deletions {
// do not do delete already merged PRs
-
if p.State == db.PullMerged {
continue
}
···
np, _ := newById[id]
// do not update already merged PRs
-
if op.State == db.PullMerged {
continue
}
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.")
return
}
-
var pullsToMerge db.Stack
pullsToMerge = append(pullsToMerge, pull)
if pull.IsStacked() {
-
stack, ok := r.Context().Value("stack").(db.Stack)
if !ok {
log.Println("failed to get stack")
s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.")
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
defer tx.Rollback()
-
var pullsToClose []*db.Pull
pullsToClose = append(pullsToClose, pull)
// if this PR is stacked, then we want to close all PRs below this one on the stack
if pull.IsStacked() {
-
stack := r.Context().Value("stack").(db.Stack)
subStack := stack.StrictlyBelow(pull)
pullsToClose = append(pullsToClose, subStack...)
}
···
return
}
-
pull, ok := r.Context().Value("pull").(*db.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
defer tx.Rollback()
-
var pullsToReopen []*db.Pull
pullsToReopen = append(pullsToReopen, pull)
// if this PR is stacked, then we want to reopen all PRs above this one on the stack
if pull.IsStacked() {
-
stack := r.Context().Value("stack").(db.Stack)
subStack := stack.StrictlyAbove(pull)
pullsToReopen = append(pullsToReopen, subStack...)
}
···
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
-
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *db.PullSource, stackId string) (db.Stack, error) {
formatPatches, err := patchutil.ExtractPatches(patch)
if err != nil {
return nil, fmt.Errorf("Failed to extract patches: %v", err)
···
}
// the stack is identified by a UUID
-
var stack db.Stack
parentChangeId := ""
for _, fp := range formatPatches {
// all patches must have a jj change-id
···
body := fp.Body
rkey := tid.TID()
-
initialSubmission := db.PullSubmission{
Patch: fp.Raw,
SourceRev: fp.SHA,
}
-
pull := db.Pull{
Title: title,
Body: body,
TargetBranch: targetBranch,
OwnerDid: user.Did,
RepoAt: f.RepoAt(),
Rkey: rkey,
-
Submissions: []*db.PullSubmission{
&initialSubmission,
},
PullSource: pullSource,
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
// can be nil if this pull is not stacked
+
stack, _ := r.Context().Value("stack").(models.Stack)
roundNumberStr := chi.URLParam(r, "round")
roundNumber, err := strconv.Atoi(roundNumberStr)
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
// can be nil if this pull is not stacked
+
stack, _ := r.Context().Value("stack").(models.Stack)
+
abandonedPulls, _ := r.Context().Value("abandonedPulls").([]*models.Pull)
totalIdents := 1
for _, submission := range pull.Submissions {
···
})
}
+
func (s *Pulls) mergeCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *models.Pull, stack models.Stack) types.MergeCheckResponse {
+
if pull.State == models.PullMerged {
return types.MergeCheckResponse{}
}
···
return result
}
+
func (s *Pulls) resubmitCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *models.Pull, stack models.Stack) pages.ResubmitResult {
+
if pull.State == models.PullMerged || pull.State == models.PullDeleted || pull.PullSource == nil {
return pages.Unknown
}
···
diffOpts.Split = true
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
return
}
+
stack, _ := r.Context().Value("stack").(models.Stack)
roundId := chi.URLParam(r, "round")
roundIdInt, err := strconv.Atoi(roundId)
···
diffOpts.Split = true
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to get pull.")
···
}
func (s *Pulls) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) {
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
user := s.oauth.GetUser(r)
params := r.URL.Query()
+
state := models.PullOpen
switch params.Get("state") {
case "closed":
+
state = models.PullClosed
case "merged":
+
state = models.PullMerged
}
f, err := s.repoResolver.Resolve(r)
···
}
// we want to group all stacked PRs into just one list
+
stacks := make(map[string]models.Stack)
var shas []string
n := 0
for _, p := range pulls {
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
return
}
+
comment := &models.PullComment{
OwnerDid: user.Did,
RepoAt: f.RepoAt().String(),
PullId: pull.PullId,
···
return
}
+
pullSource := &models.PullSource{
Branch: sourceBranch,
}
recordPullSource := &tangled.RepoPull_Source{
···
forkAtUri := fork.RepoAt()
forkAtUriStr := forkAtUri.String()
+
pullSource := &models.PullSource{
Branch: sourceBranch,
RepoAt: &forkAtUri,
}
···
title, body, targetBranch string,
patch string,
sourceRev string,
+
pullSource *models.PullSource,
recordPullSource *tangled.RepoPull_Source,
isStacked bool,
) {
···
}
rkey := tid.TID()
+
initialSubmission := models.PullSubmission{
Patch: patch,
SourceRev: sourceRev,
}
+
pull := &models.Pull{
Title: title,
Body: body,
TargetBranch: targetBranch,
OwnerDid: user.Did,
RepoAt: f.RepoAt(),
Rkey: rkey,
+
Submissions: []*models.PullSubmission{
&initialSubmission,
},
PullSource: pullSource,
···
targetBranch string,
patch string,
sourceRev string,
+
pullSource *models.PullSource,
) {
// run some necessary checks for stacked-prs first
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitPatch(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitBranch(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
···
func (s *Pulls) resubmitFork(w http.ResponseWriter, r *http.Request) {
user := s.oauth.GetUser(r)
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
···
}
// validate a resubmission against a pull request
+
func validateResubmittedPatch(pull *models.Pull, patch string) error {
if patch == "" {
return fmt.Errorf("Patch is empty.")
}
···
r *http.Request,
f *reporesolver.ResolvedRepo,
user *oauth.User,
+
pull *models.Pull,
patch string,
sourceRev string,
) {
···
r *http.Request,
f *reporesolver.ResolvedRepo,
user *oauth.User,
+
pull *models.Pull,
patch string,
stackId string,
) {
targetBranch := pull.TargetBranch
+
origStack, _ := r.Context().Value("stack").(models.Stack)
newStack, err := newStack(f, user, targetBranch, patch, pull.PullSource, stackId)
if err != nil {
log.Println("failed to create resubmitted stack", err)
···
}
// find the diff between the stacks, first, map them by changeId
+
origById := make(map[string]*models.Pull)
+
newById := make(map[string]*models.Pull)
for _, p := range origStack {
origById[p.ChangeId] = p
}
···
// commits that got updated: corresponding pull is resubmitted & new round begins
//
// for commits that were unchanged: no changes, parent-change-id is updated as necessary
+
additions := make(map[string]*models.Pull)
+
deletions := make(map[string]*models.Pull)
unchanged := make(map[string]struct{})
updated := make(map[string]struct{})
···
// deleted pulls are marked as deleted in the DB
for _, p := range deletions {
// do not do delete already merged PRs
+
if p.State == models.PullMerged {
continue
}
···
np, _ := newById[id]
// do not update already merged PRs
+
if op.State == models.PullMerged {
continue
}
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.")
return
}
+
var pullsToMerge models.Stack
pullsToMerge = append(pullsToMerge, pull)
if pull.IsStacked() {
+
stack, ok := r.Context().Value("stack").(models.Stack)
if !ok {
log.Println("failed to get stack")
s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.")
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
defer tx.Rollback()
+
var pullsToClose []*models.Pull
pullsToClose = append(pullsToClose, pull)
// if this PR is stacked, then we want to close all PRs below this one on the stack
if pull.IsStacked() {
+
stack := r.Context().Value("stack").(models.Stack)
subStack := stack.StrictlyBelow(pull)
pullsToClose = append(pullsToClose, subStack...)
}
···
return
}
+
pull, ok := r.Context().Value("pull").(*models.Pull)
if !ok {
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
}
defer tx.Rollback()
+
var pullsToReopen []*models.Pull
pullsToReopen = append(pullsToReopen, pull)
// if this PR is stacked, then we want to reopen all PRs above this one on the stack
if pull.IsStacked() {
+
stack := r.Context().Value("stack").(models.Stack)
subStack := stack.StrictlyAbove(pull)
pullsToReopen = append(pullsToReopen, subStack...)
}
···
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
+
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {
formatPatches, err := patchutil.ExtractPatches(patch)
if err != nil {
return nil, fmt.Errorf("Failed to extract patches: %v", err)
···
}
// the stack is identified by a UUID
+
var stack models.Stack
parentChangeId := ""
for _, fp := range formatPatches {
// all patches must have a jj change-id
···
body := fp.Body
rkey := tid.TID()
+
initialSubmission := models.PullSubmission{
Patch: fp.Raw,
SourceRev: fp.SHA,
}
+
pull := models.Pull{
Title: title,
Body: body,
TargetBranch: targetBranch,
OwnerDid: user.Did,
RepoAt: f.RepoAt(),
Rkey: rkey,
+
Submissions: []*models.PullSubmission{
&initialSubmission,
},
PullSource: pullSource,
+5 -5
appview/repo/feed.go
···
return feed, nil
}
-
func (rp *Repo) createPullItems(ctx context.Context, pull *db.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
if err != nil {
return nil, err
···
}, nil
}
-
func (rp *Repo) getPullState(pull *db.Pull) string {
-
if pull.State == db.PullOpen {
return "opened"
}
return pull.State.String()
}
-
func (rp *Repo) buildPullDescription(handle syntax.Handle, state string, pull *db.Pull, repoName string) string {
base := fmt.Sprintf("@%s %s pull request #%d", handle, state, pull.PullId)
-
if pull.State == db.PullMerged {
return fmt.Sprintf("%s (on round #%d) in %s", base, pull.LastRoundNumber(), repoName)
}
···
return feed, nil
}
+
func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
if err != nil {
return nil, err
···
}, nil
}
+
func (rp *Repo) getPullState(pull *models.Pull) string {
+
if pull.State == models.PullOpen {
return "opened"
}
return pull.State.String()
}
+
func (rp *Repo) buildPullDescription(handle syntax.Handle, state string, pull *models.Pull, repoName string) string {
base := fmt.Sprintf("@%s %s pull request #%d", handle, state, pull.PullId)
+
if pull.State == models.PullMerged {
return fmt.Sprintf("%s (on round #%d) in %s", base, pull.LastRoundNumber(), repoName)
}
+2 -2
appview/state/profile.go
···
return &feed, nil
}
-
func (s *State) addPullRequestItems(ctx context.Context, feed *feeds.Feed, pulls []*db.Pull, author *feeds.Author) error {
for _, pull := range pulls {
owner, err := s.idResolver.ResolveIdent(ctx, pull.Repo.Did)
if err != nil {
···
return nil
}
-
func (s *State) createPullRequestItem(pull *db.Pull, owner *identity.Identity, author *feeds.Author) *feeds.Item {
return &feeds.Item{
Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name),
Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.AppviewHost, owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"},
···
return &feed, nil
}
+
func (s *State) addPullRequestItems(ctx context.Context, feed *feeds.Feed, pulls []*models.Pull, author *feeds.Author) error {
for _, pull := range pulls {
owner, err := s.idResolver.ResolveIdent(ctx, pull.Repo.Did)
if err != nil {
···
return nil
}
+
func (s *State) createPullRequestItem(pull *models.Pull, owner *identity.Identity, author *feeds.Author) *feeds.Item {
return &feeds.Item{
Title: fmt.Sprintf("%s created pull request '%s' in @%s/%s", author.Name, pull.Title, owner.Handle, pull.Repo.Name),
Link: &feeds.Link{Href: fmt.Sprintf("%s/@%s/%s/pulls/%d", s.config.Core.AppviewHost, owner.Handle, pull.Repo.Name, pull.PullId), Type: "text/html", Rel: "alternate"},