mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
feat(milestone): milestone tables, views, forms and one selection model
milestone view printed the list row, and milestone list hid the due date and progress. milestone list now shows ID TITLE [STATE] DUE PROGRESS, open milestones first and by due date; milestone view shows the state, due date, progress, description and the milestone's web page (also --web). Every milestone command addresses milestones by title or ID. Mutations report ✓ lines: milestone create also prints the new milestone's URL and takes the title as an argument; close and reopen skip milestones already in that state; delete names the milestone in its confirmation; milestone issue add and remove report one line such as "✓ Added #1, #2 to milestone "v1.0" in alice/x". milestone issue list uses the issue table. milestone edit without flags on a terminal opens the milestone form with the current values, and every milestone command completes milestone titles and has help text with examples. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
14 files changed
+606
-257
No files matched your search
@@ -5,81 +5,86 @@ package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/tea/internal/clierr"
|
||||
"gitea.dev/tea/internal/services"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// findMilestone resolves a milestone argument, a title or an ID, in the target repository.
|
||||
func findMilestone(cmd *cobra.Command, t target, raw string, byName bool) (services.Milestone, error) {
|
||||
if strings.TrimSpace(raw) == "" {
|
||||
return services.Milestone{}, clierr.Wrap(clierr.Usage, errors.New("milestone title or ID cannot be empty"))
|
||||
}
|
||||
return (services.Milestones{Client: t.client, Repository: t.Repo}).FindOne(cmd.Context(), raw, byName)
|
||||
}
|
||||
|
||||
func (a *application) milestoneIssueCommand() *cobra.Command {
|
||||
group := &cobra.Command{Use: "issue", Short: "Manage milestone issues and pull requests"}
|
||||
opts := services.ListOptions{}
|
||||
group := &cobra.Command{Use: "issue", Short: "Manage the issues and pull requests of a milestone", Long: `List the issues and pull requests in a milestone, or add them to it and remove them from
|
||||
it. Milestones are addressed by title or ID; issues and pull requests by number or URL.`}
|
||||
opts := services.ListOptions{Page: 1}
|
||||
var byName bool
|
||||
list := &cobra.Command{Use: "list <milestone>", Short: "List issues and pull requests in a milestone", Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := oneOf("state", opts.State, "open", "closed", "all"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := oneOf("kind", opts.Kind, "all", "issues", "pulls"); err != nil {
|
||||
return err
|
||||
}
|
||||
list := &cobra.Command{Use: "list <milestone>", Short: "List issues and pull requests in a milestone", Example: `$ tea milestone issue list v1.0
|
||||
$ tea milestone issue list v1.0 --state all --kind issues`, Annotations: map[string]string{"tea.arguments": milestoneArguments}, Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
target, err := a.resolve(cmd.Context(), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
milestone, err := findMilestone(cmd, target, args[0], byName)
|
||||
milestone, err := findMilestone(cmd, services.Milestones{Client: target.client, Repository: target.Repo}, args[0], byName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Milestones = strconv.FormatInt(milestone.ID, 10)
|
||||
items, more, err := (services.Issues{Client: target.client, Repository: target.Repo}).List(cmd.Context(), opts)
|
||||
page, err := (services.Issues{Client: target.client, Repository: target.Repo}).ListPage(cmd.Context(), opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeList(a, cmd, items, issueRow); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.morePagesHint(cmd, more, opts.Page)
|
||||
where := fmt.Sprintf("in milestone %q of %s", milestone.Title, target.Repo)
|
||||
return writeTable(a, cmd, page.Items, issueListSpec(where, opts.State, opts.Kind, false), servicePage(page, opts.Page))
|
||||
}}
|
||||
pageFlags(list, &opts.Limit, &opts.Page)
|
||||
list.Flags().StringVar(&opts.State, "state", "open", "open, closed, or all")
|
||||
list.Flags().StringVar(&opts.Kind, "kind", "all", "all, issues, or pulls")
|
||||
enumFlag(list, &opts.State, "state", "s", "open", "Filter by state", "open", "closed", "all")
|
||||
enumFlag(list, &opts.Kind, "kind", "", "all", "List issues, pull requests or both", "issues", "pulls", "all")
|
||||
list.Flags().BoolVar(&byName, "by-name", false, "Match a numeric argument as a milestone title rather than an ID")
|
||||
list.ValidArgsFunction = a.completeMilestoneArgs(false)
|
||||
group.AddCommand(list)
|
||||
for _, verb := range []string{"add", "remove"} {
|
||||
var byName bool
|
||||
command := &cobra.Command{Use: verb + " <milestone> <number>...", Short: titleCase(verb) + " issues or pull requests in a milestone", Args: usageArgs(cobra.MinimumNArgs(2)), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
numbers, err := resourceNumbers(args[1:])
|
||||
short, preposition, past := "Add issues or pull requests to a milestone", "to", "Added"
|
||||
if verb == "remove" {
|
||||
short, preposition, past = "Remove issues or pull requests from a milestone", "from", "Removed"
|
||||
}
|
||||
command := &cobra.Command{Use: verb + " <milestone> {<number> | <url>}...", Short: short, Example: fmt.Sprintf(`$ tea milestone issue %[1]s v1.0 12
|
||||
$ tea milestone issue %[1]s v1.0 12 13 '#14'`, verb), Annotations: map[string]string{"tea.arguments": milestoneArguments + "\n\n" + issueArguments}, Args: usageArgs(cobra.MinimumNArgs(2)), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
target, numbers, err := a.resolveRefs(cmd, args[1:], issueRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target, err := a.resolve(cmd.Context(), true)
|
||||
milestone, err := findMilestone(cmd, services.Milestones{Client: target.client, Repository: target.Repo}, args[0], byName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
milestone, err := findMilestone(cmd, target, args[0], byName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := milestone.ID
|
||||
service := services.Issues{Client: target.client, Repository: target.Repo}
|
||||
return batch(a, cmd, numbers, issueLabel, func(n int64) (services.Issue, error) {
|
||||
results, failures := applyBatch(cmd, numbers, issueLabel, func(n int64) (services.Issue, error) {
|
||||
if verb == "remove" {
|
||||
return service.RemoveMilestone(cmd.Context(), n, id)
|
||||
return service.RemoveMilestone(cmd.Context(), n, milestone)
|
||||
}
|
||||
return service.Edit(cmd.Context(), n, services.IssueEdit{Milestone: &id})
|
||||
}, issueRow)
|
||||
return service.Edit(cmd.Context(), n, services.IssueEdit{Milestone: &milestone.ID})
|
||||
}, func(services.Issue) {})
|
||||
if len(results) > 0 {
|
||||
changed := make([]string, len(results))
|
||||
for i, item := range results {
|
||||
changed[i] = "#" + strconv.FormatInt(item.Number, 10)
|
||||
}
|
||||
a.done(cmd, "%s %s %s %s", past, strings.Join(changed, ", "), preposition, milestoneName(target, milestone))
|
||||
}
|
||||
if a.options.json != "" {
|
||||
failures = append(failures, writeBatchJSON(a, cmd, len(numbers), results))
|
||||
}
|
||||
return errors.Join(failures...)
|
||||
}}
|
||||
command.Flags().BoolVar(&byName, "by-name", false, "Match a numeric argument as a milestone title rather than an ID")
|
||||
command.Flags().BoolVar(&byName, "by-name", false, "Match a numeric milestone argument as a title rather than an ID")
|
||||
milestones, issues := a.completeMilestones(), a.completeIssues("open")
|
||||
command.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
return milestones(cmd, args, toComplete)
|
||||
}
|
||||
return issues(cmd, args, toComplete)
|
||||
}
|
||||
group.AddCommand(command)
|
||||
}
|
||||
return group
|
||||
|
||||
Reference in new issue
Block a user