fix: resolve pending and jumping
This commit is contained in:
@@ -27,28 +27,30 @@ const (
|
||||
)
|
||||
|
||||
type mutationOperation struct {
|
||||
ID string `json:"id"`
|
||||
Kind mutationKind `json:"kind"`
|
||||
Owner string `json:"owner"`
|
||||
Repository string `json:"repository"`
|
||||
Number int `json:"number"`
|
||||
PRID string `json:"pull_request_id"`
|
||||
ThreadID string `json:"thread_id,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Resolved bool `json:"resolved,omitempty"`
|
||||
Viewer string `json:"viewer,omitempty"`
|
||||
Original PullRequestMetadata `json:"original,omitempty"`
|
||||
Update PullRequestMetadata `json:"update,omitempty"`
|
||||
Permissions ViewerPermissions `json:"permissions"`
|
||||
ThreadCanReply bool `json:"thread_can_reply,omitempty"`
|
||||
ThreadCanResolve bool `json:"thread_can_resolve,omitempty"`
|
||||
ThreadCanUnresolve bool `json:"thread_can_unresolve,omitempty"`
|
||||
PeopleDone bool `json:"people_done,omitempty"`
|
||||
Attempted bool `json:"attempted,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
Ambiguous bool `json:"ambiguous,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
EnqueuedAt time.Time `json:"enqueued_at"`
|
||||
ID string `json:"id"`
|
||||
Kind mutationKind `json:"kind"`
|
||||
Owner string `json:"owner"`
|
||||
Repository string `json:"repository"`
|
||||
Number int `json:"number"`
|
||||
PRID string `json:"pull_request_id"`
|
||||
ThreadID string `json:"thread_id,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Resolved bool `json:"resolved,omitempty"`
|
||||
Viewer string `json:"viewer,omitempty"`
|
||||
Original PullRequestMetadata `json:"original,omitempty"`
|
||||
Update PullRequestMetadata `json:"update,omitempty"`
|
||||
Permissions ViewerPermissions `json:"permissions"`
|
||||
ThreadCanReply bool `json:"thread_can_reply,omitempty"`
|
||||
ThreadCanResolve bool `json:"thread_can_resolve,omitempty"`
|
||||
ThreadCanUnresolve bool `json:"thread_can_unresolve,omitempty"`
|
||||
PeopleDone bool `json:"people_done,omitempty"`
|
||||
Attempted bool `json:"attempted,omitempty"`
|
||||
AwaitingVerification bool `json:"awaiting_verification,omitempty"`
|
||||
Unverified bool `json:"unverified,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
Ambiguous bool `json:"ambiguous,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
EnqueuedAt time.Time `json:"enqueued_at"`
|
||||
}
|
||||
|
||||
type mutationQueueEnvelope struct {
|
||||
@@ -235,6 +237,7 @@ type mutationReplayState int
|
||||
|
||||
const (
|
||||
mutationReplayApplied mutationReplayState = iota
|
||||
mutationReplayVerifying
|
||||
mutationReplayWaiting
|
||||
mutationReplayBlocked
|
||||
)
|
||||
@@ -303,18 +306,51 @@ func executeQueuedMutation(
|
||||
operation mutationOperation, details PRDetails,
|
||||
) mutationReplayMsg {
|
||||
blocked := func(reason string, ambiguous bool) mutationReplayMsg {
|
||||
operation.AwaitingVerification = false
|
||||
operation.Unverified = true
|
||||
if operation.Kind == mutationPREdit {
|
||||
operation.PeopleDone = false
|
||||
}
|
||||
operation.Blocked, operation.Ambiguous, operation.LastError = true, ambiguous, reason
|
||||
if err := store.update(operation); err != nil {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayBlocked, details: details, reason: reason, err: err}
|
||||
}
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayBlocked, details: details, reason: reason}
|
||||
}
|
||||
verifying := func() mutationReplayMsg {
|
||||
operation.AwaitingVerification = true
|
||||
operation.Blocked, operation.Ambiguous, operation.LastError = false, false, ""
|
||||
if err := store.update(operation); err != nil {
|
||||
return blocked("could not checkpoint successful mutation for verification", true)
|
||||
}
|
||||
return mutationReplayMsg{
|
||||
operation: operation, state: mutationReplayVerifying, details: details,
|
||||
}
|
||||
}
|
||||
markUnverified := func() *mutationReplayMsg {
|
||||
if !operation.AwaitingVerification {
|
||||
return nil
|
||||
}
|
||||
operation.AwaitingVerification = false
|
||||
operation.Unverified = true
|
||||
if operation.Kind == mutationPREdit {
|
||||
operation.PeopleDone = false
|
||||
}
|
||||
if err := store.update(operation); err != nil {
|
||||
result := blocked("could not record failed mutation verification", true)
|
||||
return &result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
thread := findReviewThread(details.Threads, operation.ThreadID)
|
||||
switch operation.Kind {
|
||||
case mutationReply:
|
||||
if queuedReplyPresent(details, operation) {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
}
|
||||
if result := markUnverified(); result != nil {
|
||||
return *result
|
||||
}
|
||||
if thread == nil {
|
||||
return blocked("the review thread no longer exists", false)
|
||||
}
|
||||
@@ -339,7 +375,7 @@ func executeQueuedMutation(
|
||||
if _, err := writer.ReplyToThread(ctx, operation.ThreadID, operation.Body); err != nil {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayWaiting, details: details, err: err}
|
||||
}
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
return verifying()
|
||||
case mutationResolution:
|
||||
if thread == nil {
|
||||
return blocked("the review thread no longer exists", false)
|
||||
@@ -347,6 +383,9 @@ func executeQueuedMutation(
|
||||
if thread.IsResolved == operation.Resolved {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
}
|
||||
if result := markUnverified(); result != nil {
|
||||
return *result
|
||||
}
|
||||
if operation.Resolved && !thread.ViewerCanResolve {
|
||||
return blocked("GitHub no longer grants resolve permission for this thread", false)
|
||||
}
|
||||
@@ -368,8 +407,14 @@ func executeQueuedMutation(
|
||||
if _, err := writer.SetThreadResolved(ctx, operation.ThreadID, operation.Resolved); err != nil {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayWaiting, details: details, err: err}
|
||||
}
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
return verifying()
|
||||
case mutationPREdit:
|
||||
if queuedPREditPresent(details, operation.Update) {
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
}
|
||||
if result := markUnverified(); result != nil {
|
||||
return *result
|
||||
}
|
||||
if !details.Permissions.CanUpdatePR {
|
||||
return blocked("GitHub no longer grants permission to update this pull request", false)
|
||||
}
|
||||
@@ -404,12 +449,18 @@ func executeQueuedMutation(
|
||||
}
|
||||
details.Title, details.Body, details.BaseRef = result.Title, result.Body, result.BaseRef
|
||||
}
|
||||
return mutationReplayMsg{operation: operation, state: mutationReplayApplied, details: details}
|
||||
return verifying()
|
||||
default:
|
||||
return blocked("queued mutation has an unsupported kind", false)
|
||||
}
|
||||
}
|
||||
|
||||
func queuedPREditPresent(details PRDetails, update PullRequestMetadata) bool {
|
||||
return samePRMetadataCore(update, currentMetadata(details)) &&
|
||||
slices.Equal(normalizedLogins(update.Reviewers), normalizedLogins(details.RequestedReviewers)) &&
|
||||
slices.Equal(normalizedLogins(update.Assignees), normalizedLogins(details.Assignees))
|
||||
}
|
||||
|
||||
func findReviewThread(threads []ReviewThread, id string) *ReviewThread {
|
||||
for index := range threads {
|
||||
if threads[index].ID == id {
|
||||
@@ -494,30 +545,38 @@ func (m App) projectQueuedMutations(details PRDetails) PRDetails {
|
||||
}
|
||||
pendingID := "pending:" + operation.ID
|
||||
found := false
|
||||
for _, comment := range thread.Comments {
|
||||
found = found || comment.ID == pendingID
|
||||
for index := range thread.Comments {
|
||||
if thread.Comments[index].ID == pendingID {
|
||||
thread.Comments[index].Pending = mutationNeedsAttention(operation)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
thread.Comments = append(thread.Comments, ReviewComment{
|
||||
ID: pendingID, Author: operation.Viewer, Body: operation.Body,
|
||||
CreatedAt: operation.EnqueuedAt, Pending: true,
|
||||
CreatedAt: operation.EnqueuedAt, Pending: mutationNeedsAttention(operation),
|
||||
})
|
||||
}
|
||||
case mutationResolution:
|
||||
if thread := findReviewThread(details.Threads, operation.ThreadID); thread != nil {
|
||||
thread.IsResolved, thread.Pending = operation.Resolved, true
|
||||
thread.IsResolved = operation.Resolved
|
||||
thread.Pending = mutationNeedsAttention(operation)
|
||||
}
|
||||
case mutationPREdit:
|
||||
details.Title, details.Body, details.BaseRef = operation.Update.Title, operation.Update.Body, operation.Update.BaseRef
|
||||
details.RequestedReviewers = slices.Clone(operation.Update.Reviewers)
|
||||
details.Assignees = slices.Clone(operation.Update.Assignees)
|
||||
details.Reviewers = projectRequestedReviewers(details.Reviewers, operation.Update.Reviewers)
|
||||
details.Pending = true
|
||||
details.Pending = mutationNeedsAttention(operation)
|
||||
}
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
func mutationNeedsAttention(operation mutationOperation) bool {
|
||||
return operation.Blocked || operation.Unverified
|
||||
}
|
||||
|
||||
func (m App) projectQueuedPullRequests(prs []PullRequest) []PullRequest {
|
||||
result := slices.Clone(prs)
|
||||
for _, operation := range m.mutations.list() {
|
||||
@@ -527,7 +586,8 @@ func (m App) projectQueuedPullRequests(prs []PullRequest) []PullRequest {
|
||||
for index := range result {
|
||||
if result[index].Owner == operation.Owner && result[index].Repository == operation.Repository &&
|
||||
result[index].Number == operation.Number {
|
||||
result[index].Title, result[index].Pending = operation.Update.Title, true
|
||||
result[index].Title = operation.Update.Title
|
||||
result[index].Pending = mutationNeedsAttention(operation)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,7 +652,8 @@ func (m *App) resolveBlockedMutation(choice int) tea.Cmd {
|
||||
}
|
||||
return command
|
||||
case "Retry this mutation now":
|
||||
operation.Attempted, operation.Blocked = false, false
|
||||
operation.Attempted, operation.AwaitingVerification = false, false
|
||||
operation.Unverified, operation.Blocked = false, false
|
||||
operation.Ambiguous, operation.LastError = false, ""
|
||||
if err := m.mutations.update(operation); err != nil {
|
||||
m.err = err
|
||||
|
||||
Reference in New Issue
Block a user