mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-31 01:05:26 +01:00 
			
		
		
		
	 b868d30434
			
		
	
	b868d30434
	
	
	
		
			
			Co-authored-by: techknowlogick <hello@techknowlogick.com> Co-committed-by: techknowlogick <hello@techknowlogick.com>
		
			
				
	
	
		
			259 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2020 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package print
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"code.gitea.io/sdk/gitea"
 | |
| )
 | |
| 
 | |
| var ciStatusSymbols = map[gitea.StatusState]string{
 | |
| 	gitea.StatusSuccess: "✓ ",
 | |
| 	gitea.StatusPending: "⭮ ",
 | |
| 	gitea.StatusWarning: "⚠ ",
 | |
| 	gitea.StatusError:   "✘ ",
 | |
| 	gitea.StatusFailure: "❌ ",
 | |
| }
 | |
| 
 | |
| // PullDetails print an pull rendered to stdout
 | |
| func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *gitea.CombinedStatus) {
 | |
| 	base := pr.Base.Name
 | |
| 	head := formatPRHead(pr)
 | |
| 	state := formatPRState(pr)
 | |
| 
 | |
| 	out := fmt.Sprintf(
 | |
| 		"# #%d %s (%s)\n@%s created %s\t**%s** <- **%s**\n\n%s\n\n",
 | |
| 		pr.Index,
 | |
| 		pr.Title,
 | |
| 		state,
 | |
| 		pr.Poster.UserName,
 | |
| 		FormatTime(*pr.Created, false),
 | |
| 		base,
 | |
| 		head,
 | |
| 		pr.Body,
 | |
| 	)
 | |
| 
 | |
| 	if ciStatus != nil || len(reviews) != 0 || pr.State == gitea.StateOpen {
 | |
| 		out += "---\n"
 | |
| 	}
 | |
| 
 | |
| 	out += formatReviews(pr, reviews)
 | |
| 
 | |
| 	if ciStatus != nil {
 | |
| 		var summary, errors string
 | |
| 		for _, s := range ciStatus.Statuses {
 | |
| 			summary += ciStatusSymbols[s.State]
 | |
| 			if s.State != gitea.StatusSuccess {
 | |
| 				errors += fmt.Sprintf("  - [**%s**:\t%s](%s)\n", s.Context, s.Description, s.TargetURL)
 | |
| 			}
 | |
| 		}
 | |
| 		if len(ciStatus.Statuses) != 0 {
 | |
| 			out += fmt.Sprintf("- CI: %s\n%s", summary, errors)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if pr.State == gitea.StateOpen {
 | |
| 		if pr.Mergeable {
 | |
| 			out += "- No Conflicts\n"
 | |
| 		} else {
 | |
| 			out += "- **Conflicting files**\n"
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if pr.AllowMaintainerEdit {
 | |
| 		out += "- Maintainers are allowed to edit\n"
 | |
| 	}
 | |
| 
 | |
| 	outputMarkdown(out, getRepoURL(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(pr *gitea.PullRequest, reviews []*gitea.PullReview) string {
 | |
| 	result := ""
 | |
| 	if len(reviews) == 0 {
 | |
| 		return result
 | |
| 	}
 | |
| 
 | |
| 	// deduplicate reviews by user (via review time & userID),
 | |
| 	reviewByUserOrTeam := make(map[string]*gitea.PullReview)
 | |
| 	for _, review := range reviews {
 | |
| 		switch review.State {
 | |
| 		case gitea.ReviewStateApproved,
 | |
| 			gitea.ReviewStateRequestChanges,
 | |
| 			gitea.ReviewStateRequestReview:
 | |
| 			if review.Reviewer != nil {
 | |
| 				if r, ok := reviewByUserOrTeam[fmt.Sprintf("user_%d", review.Reviewer.ID)]; !ok || review.Submitted.After(r.Submitted) {
 | |
| 					reviewByUserOrTeam[fmt.Sprintf("user_%d", review.Reviewer.ID)] = review
 | |
| 				}
 | |
| 			} else if review.ReviewerTeam != nil {
 | |
| 				if r, ok := reviewByUserOrTeam[fmt.Sprintf("team_%d", review.ReviewerTeam.ID)]; !ok || review.Submitted.After(r.Submitted) {
 | |
| 					reviewByUserOrTeam[fmt.Sprintf("team_%d", review.ReviewerTeam.ID)] = review
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// group reviews by type
 | |
| 	reviewByState := make(map[gitea.ReviewStateType][]string)
 | |
| 	for _, r := range reviewByUserOrTeam {
 | |
| 		if r.Reviewer != nil {
 | |
| 			reviewByState[r.State] = append(reviewByState[r.State],
 | |
| 				r.Reviewer.UserName,
 | |
| 			)
 | |
| 		} else if r.ReviewerTeam != nil {
 | |
| 			// only pulls to orgs can have team reviews
 | |
| 			org := pr.Base.Repository.Owner
 | |
| 			reviewByState[r.State] = append(reviewByState[r.State],
 | |
| 				fmt.Sprintf("%s/%s", org.UserName, r.ReviewerTeam.Name),
 | |
| 			)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// stringify
 | |
| 	for state, user := range reviewByState {
 | |
| 		result += fmt.Sprintf("- %s by @%s\n", state, strings.Join(user, ", @"))
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // PullsList prints a listing of pulls
 | |
| func PullsList(prs []*gitea.PullRequest, output string, fields []string) {
 | |
| 	printPulls(prs, output, fields)
 | |
| }
 | |
| 
 | |
| // 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, "")
 | |
| 			}
 | |
| 		}
 | |
| 		// 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, machineReadable)
 | |
| 	case "updated":
 | |
| 		return FormatTime(*x.Updated, machineReadable)
 | |
| 	case "deadline":
 | |
| 		if x.Deadline == nil {
 | |
| 			return ""
 | |
| 		}
 | |
| 		return FormatTime(*x.Deadline, machineReadable)
 | |
| 	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 ""
 | |
| }
 |