@@ -3,6 +3,8 @@ package edit
33import (
44 "fmt"
55 "net/http"
6+ "slices"
7+ "strings"
68 "time"
79
810 "github.com/MakeNowJust/heredoc"
@@ -13,7 +15,7 @@ import (
1315 shared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
1416 "github.com/cli/cli/v2/pkg/cmdutil"
1517 "github.com/cli/cli/v2/pkg/iostreams"
16- "github.com/shurcooL/githubv4 "
18+ "github.com/cli/cli/v2/pkg/set "
1719 "github.com/spf13/cobra"
1820 "golang.org/x/sync/errgroup"
1921)
@@ -170,7 +172,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
170172 }
171173
172174 if opts .Interactive && ! opts .IO .CanPrompt () {
173- return cmdutil .FlagErrorf ("--tile , --body, --reviewer, --assignee, --label, --project, or --milestone required when not running interactively" )
175+ return cmdutil .FlagErrorf ("--title , --body, --reviewer, --assignee, --label, --project, or --milestone required when not running interactively" )
174176 }
175177
176178 if runF != nil {
@@ -237,7 +239,7 @@ func editRun(opts *EditOptions) error {
237239
238240 findOptions := shared.FindOptions {
239241 Selector : opts .SelectorArg ,
240- Fields : []string {"id" , "url" , "title" , "body" , "baseRefName" , "reviewRequests" , "labels" , "projectCards" , "projectItems" , "milestone" },
242+ Fields : []string {"id" , "author" , " url" , "title" , "body" , "baseRefName" , "reviewRequests" , "labels" , "projectCards" , "projectItems" , "milestone" },
241243 Detector : opts .Detector ,
242244 }
243245
@@ -298,6 +300,15 @@ func editRun(opts *EditOptions) error {
298300 }
299301
300302 if opts .Interactive {
303+ // Remove PR author from reviewer options;
304+ // REST API errors if author is included (GraphQL silently ignores).
305+ if editable .Reviewers .Edited {
306+ s := set .NewStringSet ()
307+ s .AddValues (editable .Reviewers .Options )
308+ s .Remove (pr .Author .Login )
309+ editable .Reviewers .Options = s .ToSlice ()
310+ }
311+
301312 editorCommand , err := opts .EditorRetriever .Retrieve ()
302313 if err != nil {
303314 return err
@@ -309,7 +320,7 @@ func editRun(opts *EditOptions) error {
309320 }
310321
311322 opts .IO .StartProgressIndicator ()
312- err = updatePullRequest (httpClient , repo , pr .ID , editable )
323+ err = updatePullRequest (httpClient , repo , pr .ID , pr . Number , editable )
313324 opts .IO .StopProgressIndicator ()
314325 if err != nil {
315326 return err
@@ -320,36 +331,53 @@ func editRun(opts *EditOptions) error {
320331 return nil
321332}
322333
323- func updatePullRequest (httpClient * http.Client , repo ghrepo.Interface , id string , editable shared.Editable ) error {
334+ func updatePullRequest (httpClient * http.Client , repo ghrepo.Interface , id string , number int , editable shared.Editable ) error {
324335 var wg errgroup.Group
325336 wg .Go (func () error {
326337 return shared .UpdateIssue (httpClient , repo , id , true , editable )
327338 })
328339 if editable .Reviewers .Edited {
329340 wg .Go (func () error {
330- return updatePullRequestReviews (httpClient , repo , id , editable )
341+ return updatePullRequestReviews (httpClient , repo , number , editable )
331342 })
332343 }
333344 return wg .Wait ()
334345}
335346
336- func updatePullRequestReviews (httpClient * http.Client , repo ghrepo.Interface , id string , editable shared.Editable ) error {
337- userIds , teamIds , err := editable .ReviewerIds ()
338- if err != nil {
339- return err
340- }
341- if userIds == nil && teamIds == nil {
347+ func updatePullRequestReviews (httpClient * http.Client , repo ghrepo.Interface , number int , editable shared.Editable ) error {
348+ if ! editable .Reviewers .Edited {
342349 return nil
343350 }
344- union := githubv4 .Boolean (false )
345- reviewsRequestParams := githubv4.RequestReviewsInput {
346- PullRequestID : id ,
347- Union : & union ,
348- UserIDs : ghIds (userIds ),
349- TeamIDs : ghIds (teamIds ),
351+
352+ // Rebuild the Value slice from non-interactive flag input.
353+ if len (editable .Reviewers .Add ) != 0 || len (editable .Reviewers .Remove ) != 0 {
354+ s := set .NewStringSet ()
355+ s .AddValues (editable .Reviewers .Add )
356+ s .AddValues (editable .Reviewers .Default )
357+ s .RemoveValues (editable .Reviewers .Remove )
358+ editable .Reviewers .Value = s .ToSlice ()
359+ }
360+
361+ addUsers , addTeams := partitionUsersAndTeams (editable .Reviewers .Value )
362+
363+ // Reviewers in Default but not in the Value have been removed interactively.
364+ var toRemove []string
365+ for _ , r := range editable .Reviewers .Default {
366+ if ! slices .Contains (editable .Reviewers .Value , r ) {
367+ toRemove = append (toRemove , r )
368+ }
350369 }
370+ removeUsers , removeTeams := partitionUsersAndTeams (toRemove )
371+
351372 client := api .NewClientFromHTTP (httpClient )
352- return api .UpdatePullRequestReviews (client , repo , reviewsRequestParams )
373+ wg := errgroup.Group {}
374+ wg .Go (func () error {
375+ return api .AddPullRequestReviews (client , repo , number , addUsers , addTeams )
376+ })
377+ wg .Go (func () error {
378+ return api .RemovePullRequestReviews (client , repo , number , removeUsers , removeTeams )
379+ })
380+ return wg .Wait ()
353381}
354382
355383type Surveyor interface {
@@ -391,13 +419,18 @@ func (e editorRetriever) Retrieve() (string, error) {
391419 return cmdutil .DetermineEditor (e .config )
392420}
393421
394- func ghIds (s * []string ) * []githubv4.ID {
395- if s == nil {
396- return nil
397- }
398- ids := make ([]githubv4.ID , len (* s ))
399- for i , v := range * s {
400- ids [i ] = v
422+ // partitionUsersAndTeams splits reviewer identifiers into user logins and team slugs.
423+ // Team identifiers are in the form "org/slug"; only the slug portion is returned for teams.
424+ func partitionUsersAndTeams (values []string ) (users []string , teams []string ) {
425+ for _ , v := range values {
426+ if strings .ContainsRune (v , '/' ) {
427+ parts := strings .SplitN (v , "/" , 2 )
428+ if len (parts ) == 2 && parts [1 ] != "" {
429+ teams = append (teams , parts [1 ])
430+ }
431+ } else if v != "" {
432+ users = append (users , v )
433+ }
401434 }
402- return & ids
435+ return
403436}
0 commit comments