mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-30 16:55:25 +01:00 
			
		
		
		
	PR listing: add --fields & expose additional fields (#415)
This PR adds the `--fields` flag to `tea pr ls` (#342), and exposes more fields specific to the `PullRequest` type: ``` --fields value, -f value Comma-separated list of fields to print. Available values: index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments (default: "index,title,state,author,milestone,updated,labels") ``` Co-authored-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com> Co-authored-by: Norwin <git@nroo.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/415 Reviewed-by: Norwin <noerw@noreply.gitea.io> Reviewed-by: techknowlogick <techknowlogick@gitea.io> Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: justusbunsi <justusbunsi@noreply.gitea.io> Co-committed-by: justusbunsi <justusbunsi@noreply.gitea.io>
This commit is contained in:
		| @@ -13,6 +13,10 @@ import ( | ||||
| 	"github.com/urfave/cli/v2" | ||||
| ) | ||||
|  | ||||
| var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{ | ||||
| 	"index", "title", "state", "author", "milestone", "updated", "labels", | ||||
| }) | ||||
|  | ||||
| // CmdPullsList represents a sub command of issues to list pulls | ||||
| var CmdPullsList = cli.Command{ | ||||
| 	Name:        "list", | ||||
| @@ -20,7 +24,7 @@ var CmdPullsList = cli.Command{ | ||||
| 	Usage:       "List pull requests of the repository", | ||||
| 	Description: `List pull requests of the repository`, | ||||
| 	Action:      RunPullsList, | ||||
| 	Flags:       flags.IssuePRFlags, | ||||
| 	Flags:       append([]cli.Flag{pullFieldsFlag}, flags.IssuePRFlags...), | ||||
| } | ||||
|  | ||||
| // RunPullsList return list of pulls | ||||
| @@ -46,6 +50,11 @@ func RunPullsList(cmd *cli.Context) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	print.PullsList(prs, ctx.Output) | ||||
| 	fields, err := pullFieldsFlag.GetValues(cmd) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	print.PullsList(prs, ctx.Output, fields) | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -72,3 +72,16 @@ func formatUserName(u *gitea.User) string { | ||||
| 	} | ||||
| 	return u.FullName | ||||
| } | ||||
|  | ||||
| func formatBoolean(b bool, allowIcons bool) string { | ||||
| 	if !allowIcons { | ||||
| 		return fmt.Sprintf("%v", b) | ||||
| 	} | ||||
|  | ||||
| 	styled := "✔" | ||||
| 	if !b { | ||||
| 		styled = "✖" | ||||
| 	} | ||||
|  | ||||
| 	return styled | ||||
| } | ||||
|   | ||||
| @@ -54,19 +54,20 @@ var IssueFields = []string{ | ||||
| func printIssues(issues []*gitea.Issue, output string, fields []string) { | ||||
| 	labelMap := map[int64]string{} | ||||
| 	var printables = make([]printable, len(issues)) | ||||
| 	machineReadable := isMachineReadable(output) | ||||
|  | ||||
| 	for i, x := range issues { | ||||
| 		// pre-serialize labels for performance | ||||
| 		for _, label := range x.Labels { | ||||
| 			if _, ok := labelMap[label.ID]; !ok { | ||||
| 				labelMap[label.ID] = formatLabel(label, !isMachineReadable(output), "") | ||||
| 				labelMap[label.ID] = formatLabel(label, !machineReadable, "") | ||||
| 			} | ||||
| 		} | ||||
| 		// store items with printable interface | ||||
| 		printables[i] = &printableIssue{x, &labelMap} | ||||
| 	} | ||||
|  | ||||
| 	t := tableFromItems(fields, printables) | ||||
| 	t := tableFromItems(fields, printables, machineReadable) | ||||
| 	t.print(output) | ||||
| } | ||||
|  | ||||
| @@ -75,7 +76,7 @@ type printableIssue struct { | ||||
| 	formattedLabels *map[int64]string | ||||
| } | ||||
|  | ||||
| func (x printableIssue) FormatField(field string) string { | ||||
| func (x printableIssue) FormatField(field string, machineReadable bool) string { | ||||
| 	switch field { | ||||
| 	case "index": | ||||
| 		return fmt.Sprintf("%d", x.Index) | ||||
|   | ||||
| @@ -6,7 +6,6 @@ package print | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"code.gitea.io/sdk/gitea" | ||||
| @@ -23,19 +22,8 @@ var ciStatusSymbols = map[gitea.StatusState]string{ | ||||
| // PullDetails print an pull rendered to stdout | ||||
| func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *gitea.CombinedStatus) { | ||||
| 	base := pr.Base.Name | ||||
| 	head := pr.Head.Name | ||||
| 	if pr.Head.RepoID != pr.Base.RepoID { | ||||
| 		if pr.Head.Repository != nil { | ||||
| 			head = pr.Head.Repository.Owner.UserName + ":" + head | ||||
| 		} else { | ||||
| 			head = "delete:" + head | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	state := pr.State | ||||
| 	if pr.Merged != nil { | ||||
| 		state = "merged" | ||||
| 	} | ||||
| 	head := formatPRHead(pr) | ||||
| 	state := formatPRState(pr) | ||||
|  | ||||
| 	out := fmt.Sprintf( | ||||
| 		"# #%d %s (%s)\n@%s created %s\t**%s** <- **%s**\n\n%s\n\n", | ||||
| @@ -79,6 +67,25 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g | ||||
| 	outputMarkdown(out, pr.HTMLURL) | ||||
| } | ||||
|  | ||||
| func formatPRHead(pr *gitea.PullRequest) string { | ||||
| 	head := pr.Head.Name | ||||
| 	if pr.Head.RepoID != pr.Base.RepoID { | ||||
| 		if pr.Head.Repository != nil { | ||||
| 			head = pr.Head.Repository.Owner.UserName + ":" + head | ||||
| 		} else { | ||||
| 			head = "delete:" + head | ||||
| 		} | ||||
| 	} | ||||
| 	return head | ||||
| } | ||||
|  | ||||
| func formatPRState(pr *gitea.PullRequest) string { | ||||
| 	if pr.Merged != nil { | ||||
| 		return "merged" | ||||
| 	} | ||||
| 	return string(pr.State) | ||||
| } | ||||
|  | ||||
| func formatReviews(reviews []*gitea.PullReview) string { | ||||
| 	result := "" | ||||
| 	if len(reviews) == 0 { | ||||
| @@ -114,37 +121,120 @@ func formatReviews(reviews []*gitea.PullReview) string { | ||||
| } | ||||
|  | ||||
| // PullsList prints a listing of pulls | ||||
| func PullsList(prs []*gitea.PullRequest, output string) { | ||||
| 	t := tableWithHeader( | ||||
| 		"Index", | ||||
| 		"Title", | ||||
| 		"State", | ||||
| 		"Author", | ||||
| 		"Milestone", | ||||
| 		"Updated", | ||||
| 	) | ||||
| func PullsList(prs []*gitea.PullRequest, output string, fields []string) { | ||||
| 	printPulls(prs, output, fields) | ||||
| } | ||||
|  | ||||
| 	for _, pr := range prs { | ||||
| 		if pr == nil { | ||||
| 			continue | ||||
| // PullFields are all available fields to print with PullsList() | ||||
| var PullFields = []string{ | ||||
| 	"index", | ||||
| 	"state", | ||||
| 	"author", | ||||
| 	"author-id", | ||||
| 	"url", | ||||
|  | ||||
| 	"title", | ||||
| 	"body", | ||||
|  | ||||
| 	"mergeable", | ||||
| 	"base", | ||||
| 	"base-commit", | ||||
| 	"head", | ||||
| 	"diff", | ||||
| 	"patch", | ||||
|  | ||||
| 	"created", | ||||
| 	"updated", | ||||
| 	"deadline", | ||||
|  | ||||
| 	"assignees", | ||||
| 	"milestone", | ||||
| 	"labels", | ||||
| 	"comments", | ||||
| } | ||||
|  | ||||
| func printPulls(pulls []*gitea.PullRequest, output string, fields []string) { | ||||
| 	labelMap := map[int64]string{} | ||||
| 	var printables = make([]printable, len(pulls)) | ||||
| 	machineReadable := isMachineReadable(output) | ||||
|  | ||||
| 	for i, x := range pulls { | ||||
| 		// pre-serialize labels for performance | ||||
| 		for _, label := range x.Labels { | ||||
| 			if _, ok := labelMap[label.ID]; !ok { | ||||
| 				labelMap[label.ID] = formatLabel(label, !machineReadable, "") | ||||
| 			} | ||||
| 		} | ||||
| 		author := pr.Poster.FullName | ||||
| 		if len(author) == 0 { | ||||
| 			author = pr.Poster.UserName | ||||
| 		} | ||||
| 		mile := "" | ||||
| 		if pr.Milestone != nil { | ||||
| 			mile = pr.Milestone.Title | ||||
| 		} | ||||
| 		t.addRow( | ||||
| 			strconv.FormatInt(pr.Index, 10), | ||||
| 			pr.Title, | ||||
| 			string(pr.State), | ||||
| 			author, | ||||
| 			mile, | ||||
| 			FormatTime(*pr.Updated), | ||||
| 		) | ||||
| 		// store items with printable interface | ||||
| 		printables[i] = &printablePull{x, &labelMap} | ||||
| 	} | ||||
|  | ||||
| 	t := tableFromItems(fields, printables, machineReadable) | ||||
| 	t.print(output) | ||||
| } | ||||
|  | ||||
| type printablePull struct { | ||||
| 	*gitea.PullRequest | ||||
| 	formattedLabels *map[int64]string | ||||
| } | ||||
|  | ||||
| func (x printablePull) FormatField(field string, machineReadable bool) string { | ||||
| 	switch field { | ||||
| 	case "index": | ||||
| 		return fmt.Sprintf("%d", x.Index) | ||||
| 	case "state": | ||||
| 		return formatPRState(x.PullRequest) | ||||
| 	case "author": | ||||
| 		return formatUserName(x.Poster) | ||||
| 	case "author-id": | ||||
| 		return x.Poster.UserName | ||||
| 	case "url": | ||||
| 		return x.HTMLURL | ||||
| 	case "title": | ||||
| 		return x.Title | ||||
| 	case "body": | ||||
| 		return x.Body | ||||
| 	case "created": | ||||
| 		return FormatTime(*x.Created) | ||||
| 	case "updated": | ||||
| 		return FormatTime(*x.Updated) | ||||
| 	case "deadline": | ||||
| 		if x.Deadline == nil { | ||||
| 			return "" | ||||
| 		} | ||||
| 		return FormatTime(*x.Deadline) | ||||
| 	case "milestone": | ||||
| 		if x.Milestone != nil { | ||||
| 			return x.Milestone.Title | ||||
| 		} | ||||
| 		return "" | ||||
| 	case "labels": | ||||
| 		var labels = make([]string, len(x.Labels)) | ||||
| 		for i, l := range x.Labels { | ||||
| 			labels[i] = (*x.formattedLabels)[l.ID] | ||||
| 		} | ||||
| 		return strings.Join(labels, " ") | ||||
| 	case "assignees": | ||||
| 		var assignees = make([]string, len(x.Assignees)) | ||||
| 		for i, a := range x.Assignees { | ||||
| 			assignees[i] = formatUserName(a) | ||||
| 		} | ||||
| 		return strings.Join(assignees, " ") | ||||
| 	case "comments": | ||||
| 		return fmt.Sprintf("%d", x.Comments) | ||||
| 	case "mergeable": | ||||
| 		isMergeable := x.Mergeable && x.State == gitea.StateOpen | ||||
| 		return formatBoolean(isMergeable, !machineReadable) | ||||
| 	case "base": | ||||
| 		return x.Base.Ref | ||||
| 	case "base-commit": | ||||
| 		return x.MergeBase | ||||
| 	case "head": | ||||
| 		return formatPRHead(x.PullRequest) | ||||
| 	case "diff": | ||||
| 		return x.DiffURL | ||||
| 	case "patch": | ||||
| 		return x.PatchURL | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,7 @@ func ReposList(repos []*gitea.Repository, output string, fields []string) { | ||||
| 	for i, r := range repos { | ||||
| 		printables[i] = &printableRepo{r} | ||||
| 	} | ||||
| 	t := tableFromItems(fields, printables) | ||||
| 	t := tableFromItems(fields, printables, isMachineReadable(output)) | ||||
| 	t.print(output) | ||||
| } | ||||
|  | ||||
| @@ -107,7 +107,7 @@ var RepoFields = []string{ | ||||
|  | ||||
| type printableRepo struct{ *gitea.Repository } | ||||
|  | ||||
| func (x printableRepo) FormatField(field string) string { | ||||
| func (x printableRepo) FormatField(field string, machineReadable bool) string { | ||||
| 	switch field { | ||||
| 	case "description": | ||||
| 		return x.Description | ||||
|   | ||||
| @@ -24,16 +24,16 @@ type table struct { | ||||
|  | ||||
| // printable can be implemented for structs to put fields dynamically into a table | ||||
| type printable interface { | ||||
| 	FormatField(field string) string | ||||
| 	FormatField(field string, machineReadable bool) string | ||||
| } | ||||
|  | ||||
| // high level api to print a table of items with dynamic fields | ||||
| func tableFromItems(fields []string, values []printable) table { | ||||
| func tableFromItems(fields []string, values []printable, machineReadable bool) table { | ||||
| 	t := table{headers: fields} | ||||
| 	for _, v := range values { | ||||
| 		row := make([]string, len(fields)) | ||||
| 		for i, f := range fields { | ||||
| 			row[i] = v.FormatField(f) | ||||
| 			row[i] = v.FormatField(f, machineReadable) | ||||
| 		} | ||||
| 		t.addRowSlice(row) | ||||
| 	} | ||||
|   | ||||
| @@ -18,7 +18,7 @@ func TrackedTimesList(times []*gitea.TrackedTime, outputType string, fields []st | ||||
| 		totalDuration += t.Time | ||||
| 		printables[i] = &printableTrackedTime{t, outputType} | ||||
| 	} | ||||
| 	t := tableFromItems(fields, printables) | ||||
| 	t := tableFromItems(fields, printables, isMachineReadable(outputType)) | ||||
|  | ||||
| 	if printTotal { | ||||
| 		total := make([]string, len(fields)) | ||||
| @@ -45,7 +45,7 @@ type printableTrackedTime struct { | ||||
| 	outputFormat string | ||||
| } | ||||
|  | ||||
| func (t printableTrackedTime) FormatField(field string) string { | ||||
| func (t printableTrackedTime) FormatField(field string, machineReadable bool) string { | ||||
| 	switch field { | ||||
| 	case "id": | ||||
| 		return fmt.Sprintf("%d", t.ID) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 justusbunsi
					justusbunsi