@@ -453,6 +453,72 @@ func wantedTitleFromBranch(doltPath, dbDir, branch string) string {
453453 return strings .TrimSpace (lines [1 ])
454454}
455455
456+ // submitPRReview submits a review on the GitHub PR for the given branch.
457+ // event must be "APPROVE" or "REQUEST_CHANGES".
458+ func submitPRReview (ghPath , upstreamRepo , forkOrg , branch , event , comment string ) (string , error ) {
459+ head := forkOrg + ":" + branch
460+ prURL , number := findExistingPR (ghPath , upstreamRepo , head )
461+ if number == "" {
462+ return "" , fmt .Errorf ("no open PR found for branch %s" , branch )
463+ }
464+
465+ reviewBody , _ := json .Marshal (map [string ]string {
466+ "event" : event ,
467+ "body" : comment ,
468+ })
469+ _ , err := ghAPICall (ghPath , "POST" , fmt .Sprintf ("repos/%s/pulls/%s/reviews" , upstreamRepo , number ), string (reviewBody ))
470+ if err != nil {
471+ return "" , fmt .Errorf ("submitting review: %w" , err )
472+ }
473+ return prURL , nil
474+ }
475+
476+ // parseReviewStatus parses GitHub review list JSON into approval state.
477+ // It tracks the latest review state per user and returns two independent bools.
478+ func parseReviewStatus (data []byte ) (hasApproval , hasChangesRequested bool ) {
479+ var reviews []struct {
480+ User struct { Login string } `json:"user"`
481+ State string `json:"state"`
482+ }
483+ if err := json .Unmarshal (data , & reviews ); err != nil {
484+ return false , false
485+ }
486+
487+ latest := map [string ]string {}
488+ for _ , r := range reviews {
489+ switch r .State {
490+ case "APPROVED" , "CHANGES_REQUESTED" :
491+ latest [r .User .Login ] = r .State
492+ }
493+ }
494+
495+ for _ , state := range latest {
496+ switch state {
497+ case "APPROVED" :
498+ hasApproval = true
499+ case "CHANGES_REQUESTED" :
500+ hasChangesRequested = true
501+ }
502+ }
503+ return hasApproval , hasChangesRequested
504+ }
505+
506+ // prApprovalStatus checks the review status of a GitHub PR. Best-effort.
507+ // Silently returns (false, false) on any error.
508+ func prApprovalStatus (ghPath , upstreamRepo , forkOrg , branch string ) (hasApproval , hasChangesRequested bool ) {
509+ head := forkOrg + ":" + branch
510+ _ , number := findExistingPR (ghPath , upstreamRepo , head )
511+ if number == "" {
512+ return false , false
513+ }
514+
515+ data , err := ghAPICall (ghPath , "GET" , fmt .Sprintf ("repos/%s/pulls/%s/reviews" , upstreamRepo , number ), "" )
516+ if err != nil {
517+ return false , false
518+ }
519+ return parseReviewStatus (data )
520+ }
521+
456522// closeGitHubPR finds and closes an open GitHub PR for the given branch.
457523// Best-effort: failures print warnings but don't block the merge.
458524func closeGitHubPR (ghPath , upstreamRepo , forkOrg , forkDB , branch string , stdout io.Writer ) {
0 commit comments