improve formatting of `tea repos` (#223)

make fmt

code review

use OutputMarkdown

use FormatTime()

improved repo printing

- ReposList() now allows selection of fields
- RepoDetail() uses glamour and provides more details

Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/223
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-Authored-By: Norwin <noerw@noreply.gitea.io>
Co-Committed-By: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
Norwin 2020-10-10 01:17:31 +00:00 committed by Lunny Xiao
parent a4b792e24d
commit 6ea331ce3b
4 changed files with 143 additions and 44 deletions

View File

@ -6,11 +6,28 @@ package repos
import ( import (
"fmt" "fmt"
"strings"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
// printFieldsFlag provides a selection of fields to print
var printFieldsFlag = cli.StringFlag{
Name: "fields",
Aliases: []string{"f"},
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
%s
`, strings.Join(print.RepoFields, ",")),
Value: "owner,name,type,ssh",
}
func getFields(ctx *cli.Context) []string {
return strings.Split(ctx.String("fields"), ",")
}
var typeFilterFlag = cli.StringFlag{ var typeFilterFlag = cli.StringFlag{
Name: "type", Name: "type",
Aliases: []string{"T"}, Aliases: []string{"T"},

View File

@ -27,6 +27,7 @@ var CmdReposListFlags = append([]cli.Flag{
Required: false, Required: false,
Usage: "List your starred repos instead", Usage: "List your starred repos instead",
}, },
&printFieldsFlag,
&typeFilterFlag, &typeFilterFlag,
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
@ -79,7 +80,7 @@ func RunReposList(ctx *cli.Context) error {
reposFiltered = filterReposByType(rps, typeFilter) reposFiltered = filterReposByType(rps, typeFilter)
} }
print.ReposList(reposFiltered) print.ReposList(reposFiltered, getFields(ctx))
return nil return nil
} }

View File

@ -50,6 +50,7 @@ var CmdReposSearch = cli.Command{
Required: false, Required: false,
Usage: "Filter archived repos (true|false)", Usage: "Filter archived repos (true|false)",
}, },
&printFieldsFlag,
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
}, flags.LoginOutputFlags...), }, flags.LoginOutputFlags...),
@ -122,6 +123,6 @@ func runReposSearch(ctx *cli.Context) error {
return err return err
} }
print.ReposList(rps) print.ReposList(rps, getFields(ctx))
return nil return nil
} }

View File

@ -6,78 +6,158 @@ package print
import ( import (
"fmt" "fmt"
"log"
"strings" "strings"
"time"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
) )
type rp = *gitea.Repository
type fieldFormatter = func(*gitea.Repository) string
var (
fieldFormatters map[string]fieldFormatter
// RepoFields are the available fields to print with ReposList()
RepoFields []string
)
func init() {
fieldFormatters = map[string]fieldFormatter{
"description": func(r rp) string { return r.Description },
"forks": func(r rp) string { return fmt.Sprintf("%d", r.Forks) },
"id": func(r rp) string { return r.FullName },
"name": func(r rp) string { return r.Name },
"owner": func(r rp) string { return r.Owner.UserName },
"stars": func(r rp) string { return fmt.Sprintf("%d", r.Stars) },
"ssh": func(r rp) string { return r.SSHURL },
"updated": func(r rp) string { return FormatTime(r.Updated) },
"url": func(r rp) string { return r.HTMLURL },
"permission": func(r rp) string {
if r.Permissions.Admin {
return "admin"
} else if r.Permissions.Push {
return "write"
}
return "read"
},
"type": func(r rp) string {
if r.Fork {
return "fork"
}
if r.Mirror {
return "mirror"
}
return "source"
},
}
for f := range fieldFormatters {
RepoFields = append(RepoFields, f)
}
}
// ReposList prints a listing of the repos // ReposList prints a listing of the repos
func ReposList(rps []*gitea.Repository) { func ReposList(repos []*gitea.Repository, fields []string) {
if len(rps) == 0 { if len(repos) == 0 {
fmt.Println("No repositories found") fmt.Println("No repositories found")
return return
} }
headers := []string{ if len(fields) == 0 {
"Name", fmt.Println("No fields to print")
"Type", return
"SSH",
"Owner",
}
var values [][]string
for _, rp := range rps {
var mode = "source"
if rp.Fork {
mode = "fork"
}
if rp.Mirror {
mode = "mirror"
}
values = append(
values,
[]string{
rp.FullName,
mode,
rp.SSHURL,
rp.Owner.UserName,
},
)
} }
OutputList(flags.GlobalOutputValue, headers, values) formatters := make([]fieldFormatter, len(fields))
values := make([][]string, len(repos))
// find field format functions by header name
for i, f := range fields {
if formatter, ok := fieldFormatters[strings.ToLower(f)]; ok {
formatters[i] = formatter
} else {
log.Fatalf("invalid field '%s'", f)
}
}
// extract values from each repo and store them in 2D table
for i, repo := range repos {
values[i] = make([]string, len(formatters))
for j, format := range formatters {
values[i][j] = format(repo)
}
}
OutputList(flags.GlobalOutputValue, fields, values)
} }
// RepoDetails print an repo formatted to stdout // RepoDetails print an repo formatted to stdout
func RepoDetails(repo *gitea.Repository, topics []string) { func RepoDetails(repo *gitea.Repository, topics []string) {
output := repo.FullName title := "# " + repo.FullName
if repo.Mirror { if repo.Mirror {
output += " (mirror)" title += " (mirror)"
} }
if repo.Fork { if repo.Fork {
output += " (fork)" title += " (fork)"
} }
if repo.Archived { if repo.Archived {
output += " (archived)" title += " (archived)"
} }
if repo.Empty { if repo.Empty {
output += " (empty)" title += " (empty)"
} }
output += "\n" title += "\n"
if len(topics) != 0 {
output += "Topics: " + strings.Join(topics, ", ") + "\n" var desc string
if len(repo.Description) != 0 {
desc = fmt.Sprintf("*%s*\n\n", repo.Description)
} }
output += "\n"
output += repo.Description + "\n\n" stats := fmt.Sprintf(
output += fmt.Sprintf( "Issues: %d, Stars: %d, Forks: %d, Size: %s\n",
"Open Issues: %d, Stars: %d, Forks: %d, Size: %s\n\n",
repo.OpenIssues, repo.OpenIssues,
repo.Stars, repo.Stars,
repo.Forks, repo.Forks,
formatSize(int64(repo.Size)), formatSize(int64(repo.Size)),
) )
fmt.Print(output) // NOTE: for mirrors, this is the time the mirror was last fetched..
updated := fmt.Sprintf(
"Updated: %s (%s ago)\n",
repo.Updated.Format("2006-01-02 15:04"),
time.Now().Sub(repo.Updated).Truncate(time.Minute),
)
urls := fmt.Sprintf(
"- Browse:\t%s\n- Clone:\t%s\n",
repo.HTMLURL,
repo.SSHURL,
)
if len(repo.Website) != 0 {
urls += fmt.Sprintf("- Web:\t%s\n", repo.Website)
}
perm := fmt.Sprintf(
"- Permission:\t%s\n",
fieldFormatters["permission"](repo),
)
var tops string
if len(topics) != 0 {
tops = fmt.Sprintf("- Topics:\t%s\n", strings.Join(topics, ", "))
}
OutputMarkdown(fmt.Sprintf(
"%s%s\n%s\n%s%s%s%s",
title,
desc,
stats,
updated,
urls,
perm,
tops,
))
} }