fix(milestone): address milestones by title or ID everywhere

milestone view v2.0 failed with "invalid resource number" although its
usage promised <id-or-name>, and issue create --milestone v1.0 failed
with a strconv error. Every milestone argument (view, edit, delete,
close, reopen, milestone issue add|remove|list) and issue create|edit
-m/--milestone now accept a title or an ID: a number is an ID first,
then a title; --by-name matches numeric titles only. --milestone-name
stays as a hidden name of --milestone, and issue edit gains
--remove-milestone (as does --milestone 0).

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-12 00:14:30 +02:00
1 parent 0d013746b5
commit 1035ff551b
12 files changed
+220 -147

No files matched your search

+31 -10
View File
@@ -40,22 +40,43 @@ func (s Milestones) All(ctx context.Context) ([]Milestone, error) {
return listAll(func(page int) ([]Milestone, error) { return s.List(ctx, "all", 100, page) })
}
// FilterIDs resolves the milestones that filter an issue listing, each an ID or a title, to their
// IDs. Gitea drops filter values that name no milestone and lists every issue when none is left,
// so an unknown milestone is an error here.
func (s Milestones) FilterIDs(ctx context.Context, values []string) ([]string, error) {
// Find returns the milestones that raws address among the open and closed milestones, each by ID
// or title (D10); byName matches numeric values as titles only.
func (s Milestones) Find(ctx context.Context, raws []string, byName bool) ([]Milestone, error) {
items, err := s.All(ctx)
if err != nil {
return nil, err
}
finder := NameOrID[Milestone]{Kind: "milestone", Scope: s.Repository, ID: func(m Milestone) int64 { return m.ID }, Name: func(m Milestone) string { return m.Title }}
ids := make([]string, len(values))
for i, value := range values {
milestone, err := finder.Find(items, value)
if err != nil {
finder := NameOrID[Milestone]{Kind: "milestone", Scope: s.Repository, ID: func(m Milestone) int64 { return m.ID }, Name: func(m Milestone) string { return m.Title }, ByName: byName}
result := make([]Milestone, len(raws))
for i, raw := range raws {
if result[i], err = finder.Find(items, raw); err != nil {
return nil, err
}
ids[i] = strconv.FormatInt(milestone.ID, 10)
}
return result, nil
}
// FindOne returns the milestone that raw addresses like Find.
func (s Milestones) FindOne(ctx context.Context, raw string, byName bool) (Milestone, error) {
items, err := s.Find(ctx, []string{raw}, byName)
if err != nil {
return Milestone{}, err
}
return items[0], nil
}
// FilterIDs resolves the milestones that filter an issue listing, each an ID or a title, to their
// IDs. Gitea drops filter values that name no milestone and lists every issue when none is left,
// so an unknown milestone is an error here.
func (s Milestones) FilterIDs(ctx context.Context, values []string) ([]string, error) {
items, err := s.Find(ctx, values, false)
if err != nil {
return nil, err
}
ids := make([]string, len(items))
for i, item := range items {
ids[i] = strconv.FormatInt(item.ID, 10)
}
return ids, nil
}