refactor(pr shared): extract SpecialAssigneeReplacer for @me and Copilot expansion

The inline replaceSpecialAssigneeNames closures in AssigneeIds and
AssigneeLogins were duplicated. Extract them into an exported
SpecialAssigneeReplacer type that consolidates MeReplacer and
CopilotReplacer into a single ReplaceSlice call, parameterised by
actorAssignees and copilotUseLogin.

Adopt the new type in the issue create flow as well, replacing the
manual MeReplacer + conditional CopilotReplacer sequence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kynan Ware 2026-03-24 18:59:21 -06:00
parent 11f177a8c3
commit 4e6bc78e04
2 changed files with 40 additions and 40 deletions

View file

@ -179,18 +179,12 @@ func createRun(opts *CreateOptions) (err error) {
// Replace special values in assignees
// For web mode, @copilot should be replaced by name; otherwise, login.
assigneeSet := set.NewStringSet()
meReplacer := prShared.NewMeReplacer(apiClient, baseRepo.RepoHost())
copilotReplacer := prShared.NewCopilotReplacer(!opts.WebMode)
assignees, err := meReplacer.ReplaceSlice(opts.Assignees)
assigneeReplacer := prShared.NewSpecialAssigneeReplacer(apiClient, baseRepo.RepoHost(), issueFeatures.ActorIsAssignable, !opts.WebMode)
assignees, err := assigneeReplacer.ReplaceSlice(opts.Assignees)
if err != nil {
return err
}
// TODO actorIsAssignableCleanup
if issueFeatures.ActorIsAssignable {
assignees = copilotReplacer.ReplaceSlice(assignees)
}
assigneeSet := set.NewStringSet()
assigneeSet.AddValues(assignees)
tb := prShared.IssueMetadataState{

View file

@ -95,22 +95,7 @@ func (e Editable) AssigneeIds(client *api.Client, repo ghrepo.Interface) (*[]str
// If assignees came in from command line flags, we need to
// curate the final list of assignees from the default list.
if len(e.Assignees.Add) != 0 || len(e.Assignees.Remove) != 0 {
meReplacer := NewMeReplacer(client, repo.RepoHost())
copilotReplacer := NewCopilotReplacer(true)
replaceSpecialAssigneeNames := func(value []string) ([]string, error) {
replaced, err := meReplacer.ReplaceSlice(value)
if err != nil {
return nil, err
}
// Only suppported for actor assignees.
if e.Assignees.ActorAssignees {
replaced = copilotReplacer.ReplaceSlice(replaced)
}
return replaced, nil
}
replacer := NewSpecialAssigneeReplacer(client, repo.RepoHost(), e.Assignees.ActorAssignees, true)
assigneeSet := set.NewStringSet()
@ -128,13 +113,13 @@ func (e Editable) AssigneeIds(client *api.Client, repo ghrepo.Interface) (*[]str
assigneeSet.AddValues(e.Assignees.Default)
}
add, err := replaceSpecialAssigneeNames(e.Assignees.Add)
add, err := replacer.ReplaceSlice(e.Assignees.Add)
if err != nil {
return nil, err
}
assigneeSet.AddValues(add)
remove, err := replaceSpecialAssigneeNames(e.Assignees.Remove)
remove, err := replacer.ReplaceSlice(e.Assignees.Remove)
if err != nil {
return nil, err
}
@ -156,28 +141,18 @@ func (e Editable) AssigneeLogins(client *api.Client, repo ghrepo.Interface) ([]s
}
if len(e.Assignees.Add) != 0 || len(e.Assignees.Remove) != 0 {
meReplacer := NewMeReplacer(client, repo.RepoHost())
copilotReplacer := NewCopilotReplacer(true)
replaceSpecialAssigneeNames := func(value []string) ([]string, error) {
replaced, err := meReplacer.ReplaceSlice(value)
if err != nil {
return nil, err
}
replaced = copilotReplacer.ReplaceSlice(replaced)
return replaced, nil
}
replacer := NewSpecialAssigneeReplacer(client, repo.RepoHost(), true, true)
assigneeSet := set.NewStringSet()
assigneeSet.AddValues(e.Assignees.DefaultLogins)
add, err := replaceSpecialAssigneeNames(e.Assignees.Add)
add, err := replacer.ReplaceSlice(e.Assignees.Add)
if err != nil {
return nil, err
}
assigneeSet.AddValues(add)
remove, err := replaceSpecialAssigneeNames(e.Assignees.Remove)
remove, err := replacer.ReplaceSlice(e.Assignees.Remove)
if err != nil {
return nil, err
}
@ -189,6 +164,37 @@ func (e Editable) AssigneeLogins(client *api.Client, repo ghrepo.Interface) ([]s
return e.Assignees.Value, nil
}
// SpecialAssigneeReplacer expands special assignee names (@me, Copilot actors)
// in login slices. Use NewSpecialAssigneeReplacer to create one.
type SpecialAssigneeReplacer struct {
meReplacer *MeReplacer
copilotReplacer *CopilotReplacer
actorAssignees bool
}
// NewSpecialAssigneeReplacer creates a replacer that expands @me and (when
// actorAssignees is true) Copilot actor names in assignee slices.
// copilotUseLogin controls whether Copilot actors are replaced with their
// login (true) or display name (false, used for web mode).
func NewSpecialAssigneeReplacer(client *api.Client, host string, actorAssignees bool, copilotUseLogin bool) *SpecialAssigneeReplacer {
return &SpecialAssigneeReplacer{
meReplacer: NewMeReplacer(client, host),
copilotReplacer: NewCopilotReplacer(copilotUseLogin),
actorAssignees: actorAssignees,
}
}
func (r *SpecialAssigneeReplacer) ReplaceSlice(logins []string) ([]string, error) {
replaced, err := r.meReplacer.ReplaceSlice(logins)
if err != nil {
return nil, err
}
if r.actorAssignees {
replaced = r.copilotReplacer.ReplaceSlice(replaced)
}
return replaced, nil
}
// ProjectIds returns a slice containing IDs of projects v1 that the issue or a PR has to be linked to.
func (e Editable) ProjectIds() (*[]string, error) {
if !e.Projects.Edited {