Add headers to all tables (#8157)

This commit is contained in:
Heath Stewart 2023-10-20 02:20:02 -07:00 committed by GitHub
parent 26f3601a81
commit 7738b6187c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 516 additions and 428 deletions

6
go.mod
View file

@ -9,7 +9,7 @@ require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/charmbracelet/glamour v0.6.0
github.com/charmbracelet/lipgloss v0.5.0
github.com/cli/go-gh/v2 v2.3.0
github.com/cli/go-gh/v2 v2.4.1-0.20231019124728-ec1e1cd3e0cb
github.com/cli/oauth v1.0.1
github.com/cli/safeexec v1.0.1
github.com/cpuguy83/go-md2man/v2 v2.0.3
@ -51,8 +51,8 @@ require (
github.com/alessio/shellescape v1.4.1 // indirect
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/cli/browser v1.2.0 // indirect
github.com/cli/shurcooL-graphql v0.0.3 // indirect
github.com/cli/browser v1.3.0 // indirect
github.com/cli/shurcooL-graphql v0.0.4 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect

14
go.sum
View file

@ -21,17 +21,17 @@ github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8o
github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8=
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
github.com/cli/browser v1.2.0 h1:yvU7e9qf97kZqGFX6n2zJPHsmSObY9ske+iCvKelvXg=
github.com/cli/browser v1.2.0/go.mod h1:xFFnXLVcAyW9ni0cuo6NnrbCP75JxJ0RO7VtCBiH/oI=
github.com/cli/go-gh/v2 v2.3.0 h1:FAQAP4PaWSAJf4VSxFEIYDQ1oBIs+bKB4GXQAiRr2sQ=
github.com/cli/go-gh/v2 v2.3.0/go.mod h1:6WBUuf7LUVAc+eXYYX/nYTYURRc6M03K9cJNwBKvwT0=
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
github.com/cli/go-gh/v2 v2.4.1-0.20231019124728-ec1e1cd3e0cb h1:HeIpiv5Jf09GQA0AyABbjC7Zq55eyIxpv4/BU6ujHRk=
github.com/cli/go-gh/v2 v2.4.1-0.20231019124728-ec1e1cd3e0cb/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ=
github.com/cli/oauth v1.0.1 h1:pXnTFl/qUegXHK531Dv0LNjW4mLx626eS42gnzfXJPA=
github.com/cli/oauth v1.0.1/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00=
github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cli/shurcooL-graphql v0.0.3 h1:CtpPxyGDs136/+ZeyAfUKYmcQBjDlq5aqnrDCW5Ghh8=
github.com/cli/shurcooL-graphql v0.0.3/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA=
github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY=
github.com/cli/shurcooL-graphql v0.0.4/go.mod h1:3waN4u02FiZivIV+p1y4d0Jo1jc6BViMA73C+sZo2fk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -183,7 +183,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
@ -199,7 +198,6 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -1,6 +1,7 @@
package tableprinter
import (
"io"
"strings"
"time"
@ -12,24 +13,22 @@ import (
type TablePrinter struct {
tableprinter.TablePrinter
isTTY bool
cs *iostreams.ColorScheme
}
func (t *TablePrinter) HeaderRow(columns ...string) {
if !t.isTTY {
return
}
for _, col := range columns {
t.AddField(strings.ToUpper(col))
}
t.EndRow()
// IsTTY gets wether the TablePrinter will render to a terminal.
func (t *TablePrinter) IsTTY() bool {
return t.isTTY
}
// In tty mode display the fuzzy time difference between now and t.
// In nontty mode just display t with the time.RFC3339 format.
// AddTimeField in TTY mode displays the fuzzy time difference between now and t.
// In non-TTY mode it just displays t with the time.RFC3339 format.
func (tp *TablePrinter) AddTimeField(now, t time.Time, c func(string) string) {
tf := t.Format(time.RFC3339)
var tf string
if tp.isTTY {
tf = text.FuzzyAgo(now, t)
} else {
tf = t.Format(time.RFC3339)
}
tp.AddField(tf, tableprinter.WithColor(c))
}
@ -39,15 +38,63 @@ var (
WithColor = tableprinter.WithColor
)
func New(ios *iostreams.IOStreams) *TablePrinter {
type headerOption struct {
columns []string
}
// New creates a TablePrinter from an IOStreams.
func New(ios *iostreams.IOStreams, headers headerOption) *TablePrinter {
maxWidth := 80
isTTY := ios.IsStdoutTTY()
if isTTY {
maxWidth = ios.TerminalWidth()
}
tp := tableprinter.New(ios.Out, isTTY, maxWidth)
return &TablePrinter{
TablePrinter: tp,
isTTY: isTTY,
}
return NewWithWriter(ios.Out, isTTY, maxWidth, ios.ColorScheme(), headers)
}
// NewWithWriter creates a TablePrinter from a Writer, whether the output is a terminal, the terminal width, and more.
func NewWithWriter(w io.Writer, isTTY bool, maxWidth int, cs *iostreams.ColorScheme, headers headerOption) *TablePrinter {
tp := &TablePrinter{
TablePrinter: tableprinter.New(w, isTTY, maxWidth),
isTTY: isTTY,
cs: cs,
}
if isTTY && len(headers.columns) > 0 {
// Make sure all headers are uppercase.
for i := range headers.columns {
headers.columns[i] = strings.ToUpper(headers.columns[i])
}
// Make sure all header columns are padded - even the last one. Previously, the last header column
// was not padded. In tests cs.Enabled() is false which allows us to avoid having to fix up
// numerous tests that verify header padding.
var paddingFunc func(int, string) string
if cs.Enabled() {
paddingFunc = text.PadRight
}
tp.AddHeader(
headers.columns,
tableprinter.WithPadding(paddingFunc),
tableprinter.WithColor(cs.LightGrayUnderline),
)
}
return tp
}
// WithHeader defines the column names for a table.
// Panics if columns is nil or empty.
func WithHeader(columns ...string) headerOption {
if len(columns) == 0 {
panic("must define header columns")
}
return headerOption{columns}
}
// NoHeader disable printing or checking for a table header.
//
// Deprecated: use WithHeader unless required otherwise.
var NoHeader = headerOption{}

View file

@ -77,3 +77,7 @@ func DisplayURL(urlStr string) string {
func RemoveDiacritics(value string) string {
return text.RemoveDiacritics(value)
}
func PadRight(maxWidth int, s string) string {
return text.PadRight(maxWidth, s)
}

View file

@ -115,8 +115,7 @@ func listRun(opts *ListOptions) error {
opts.Now = time.Now()
}
tp := tableprinter.New(opts.IO)
tp.HeaderRow("ID", "KEY", "SIZE", "CREATED", "ACCESSED")
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "KEY", "SIZE", "CREATED", "ACCESSED"))
for _, cache := range result.ActionsCaches {
tp.AddField(opts.IO.ColorScheme().Cyan(fmt.Sprintf("%d", cache.Id)))
tp.AddField(cache.Key)

View file

@ -7,9 +7,9 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/codespaces/api"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -116,25 +116,23 @@ func (a *App) List(ctx context.Context, opts *listOptions, exporter cmdutil.Expo
return a.browser.Browse(fmt.Sprintf("%s/codespaces?repository_id=%d", a.apiClient.ServerURL(), codespaces[0].Repository.ID))
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(a.io)
if tp.IsTTY() {
tp.AddField("NAME", nil, nil)
tp.AddField("DISPLAY NAME", nil, nil)
if opts.orgName != "" {
tp.AddField("OWNER", nil, nil)
}
tp.AddField("REPOSITORY", nil, nil)
tp.AddField("BRANCH", nil, nil)
tp.AddField("STATE", nil, nil)
tp.AddField("CREATED AT", nil, nil)
if hasNonProdVSCSTarget {
tp.AddField("VSCS TARGET", nil, nil)
}
tp.EndRow()
headers := []string{
"NAME",
"DISPLAY NAME",
}
if opts.orgName != "" {
headers = append(headers, "OWNER")
}
headers = append(headers,
"REPOSITORY",
"BRANCH",
"STATE",
"CREATED AT",
)
if hasNonProdVSCSTarget {
headers = append(headers, "VSCS TARGET")
}
tp := tableprinter.New(a.io, tableprinter.WithHeader(headers...))
cs := a.io.ColorScheme()
for _, apiCodespace := range codespaces {
@ -158,17 +156,17 @@ func (a *App) List(ctx context.Context, opts *listOptions, exporter cmdutil.Expo
nameColor = cs.Gray
}
tp.AddField(formattedName, nil, nameColor)
tp.AddField(c.DisplayName, nil, nil)
tp.AddField(formattedName, tableprinter.WithColor(nameColor))
tp.AddField(c.DisplayName)
if opts.orgName != "" {
tp.AddField(c.Owner.Login, nil, nil)
tp.AddField(c.Owner.Login)
}
tp.AddField(c.Repository.FullName, nil, nil)
tp.AddField(c.branchWithGitStatus(), nil, cs.Cyan)
tp.AddField(c.Repository.FullName)
tp.AddField(c.branchWithGitStatus(), tableprinter.WithColor(cs.Cyan))
if c.PendingOperation {
tp.AddField(c.PendingOperationDisabledReason, nil, nameColor)
tp.AddField(c.PendingOperationDisabledReason, tableprinter.WithColor(nameColor))
} else {
tp.AddField(c.State, nil, stateColor)
tp.AddField(c.State, tableprinter.WithColor(stateColor))
}
if tp.IsTTY() {
@ -176,13 +174,13 @@ func (a *App) List(ctx context.Context, opts *listOptions, exporter cmdutil.Expo
if err != nil {
return fmt.Errorf("error parsing date %q: %w", c.CreatedAt, err)
}
tp.AddField(text.FuzzyAgoAbbr(time.Now(), ct), nil, cs.Gray)
tp.AddField(text.FuzzyAgoAbbr(time.Now(), ct), tableprinter.WithColor(cs.Gray))
} else {
tp.AddField(c.CreatedAt, nil, nil)
tp.AddField(c.CreatedAt)
}
if hasNonProdVSCSTarget {
tp.AddField(c.VSCSTarget, nil, nil)
tp.AddField(c.VSCSTarget)
}
tp.EndRow()

View file

@ -107,15 +107,7 @@ func (a *App) ListPorts(ctx context.Context, selector *CodespaceSelector, export
}
cs := a.io.ColorScheme()
tp := tableprinter.New(a.io)
if a.io.IsStdoutTTY() {
tp.AddField("LABEL")
tp.AddField("PORT")
tp.AddField("VISIBILITY")
tp.AddField("BROWSE URL")
tp.EndRow()
}
tp := tableprinter.New(a.io, tableprinter.WithHeader("LABEL", "PORT", "VISIBILITY", "BROWSE URL"))
for _, port := range portInfos {
// Convert the ACE to a friendly visibility string (private, org, public)

View file

@ -73,7 +73,8 @@ func (a *App) ViewCodespace(ctx context.Context, opts *viewOptions) error {
return opts.exporter.Write(a.io, selectedCodespace)
}
tp := tableprinter.New(a.io)
//nolint:staticcheck // SA1019: Showing NAME|VALUE headers adds nothing to table.
tp := tableprinter.New(a.io, tableprinter.NoHeader)
c := codespace{selectedCodespace}
formattedName := formatNameForVSCSTarget(c.Name, c.VSCSTarget)

View file

@ -18,7 +18,6 @@ import (
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/extensions"
"github.com/cli/cli/v2/pkg/search"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -217,9 +216,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
return false
}
tp := tableprinter.New(io)
tp.HeaderRow("", "REPO", "DESCRIPTION")
tp := tableprinter.New(io, tableprinter.WithHeader("", "REPO", "DESCRIPTION"))
for _, repo := range result.Items {
if !strings.HasPrefix(repo.Name, "gh-") {
continue
@ -270,8 +267,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
return cmdutil.NewNoResultsError("no installed extensions found")
}
cs := io.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
t := utils.NewTablePrinter(io)
t := tableprinter.New(io, tableprinter.WithHeader("NAME", "REPO", "VERSION"))
for _, c := range cmds {
// TODO consider a Repo() on Extension interface
var repo string
@ -281,13 +277,13 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
}
}
t.AddField(fmt.Sprintf("gh %s", c.Name()), nil, nil)
t.AddField(repo, nil, nil)
t.AddField(fmt.Sprintf("gh %s", c.Name()))
t.AddField(repo)
version := displayExtensionVersion(c, c.CurrentVersion())
if c.IsPinned() {
t.AddField(version, nil, cs.Cyan)
t.AddField(version, tableprinter.WithColor(cs.Cyan))
} else {
t.AddField(version, nil, nil)
t.AddField(version)
}
t.EndRow()

View file

@ -7,11 +7,11 @@ import (
"time"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/gist/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -94,9 +94,7 @@ func listRun(opts *ListOptions) error {
}
cs := opts.IO.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(opts.IO)
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "DESCRIPTION", "FILES", "VISIBILITY", "UPDATED"))
for _, gist := range gists {
fileCount := len(gist.Files)
@ -118,16 +116,14 @@ func listRun(opts *ListOptions) error {
}
}
gistTime := gist.UpdatedAt.Format(time.RFC3339)
if tp.IsTTY() {
gistTime = text.FuzzyAgo(time.Now(), gist.UpdatedAt)
}
tp.AddField(gist.ID, nil, nil)
tp.AddField(text.RemoveExcessiveWhitespace(description), nil, cs.Bold)
tp.AddField(text.Pluralize(fileCount, "file"), nil, nil)
tp.AddField(visibility, nil, visColor)
tp.AddField(gistTime, nil, cs.Gray)
tp.AddField(gist.ID)
tp.AddField(
text.RemoveExcessiveWhitespace(description),
tableprinter.WithColor(cs.Bold),
)
tp.AddField(text.Pluralize(fileCount, "file"))
tp.AddField(visibility, tableprinter.WithColor(visColor))
tp.AddTimeField(time.Now(), gist.UpdatedAt, cs.Gray)
tp.EndRow()
}

View file

@ -180,10 +180,11 @@ func Test_listRun(t *testing.T) {
)
},
wantOut: heredoc.Doc(`
1234567890 cool.txt 1 file public about 6 hours ago
4567890123 1 file public about 6 hours ago
2345678901 tea leaves thwart those who ... 2 files secret about 6 hours ago
3456789012 short desc 11 files secret about 6 hours ago
ID DESCRIPTION FILES VISIBILITY UPDATED
1234567890 cool.txt 1 file public about 6 hours ago
4567890123 1 file public about 6 hours ago
2345678901 tea leaves thwart those ... 2 files secret about 6 hours ago
3456789012 short desc 11 files secret about 6 hours ago
`),
},
{
@ -214,8 +215,9 @@ func Test_listRun(t *testing.T) {
)
},
wantOut: heredoc.Doc(`
1234567890 cool.txt 1 file public about 6 hours ago
4567890123 1 file public about 6 hours ago
ID DESCRIPTION FILES VISIBILITY UPDATED
1234567890 cool.txt 1 file public about 6 hours ago
4567890123 1 file public about 6 hours ago
`),
},
{
@ -261,8 +263,9 @@ func Test_listRun(t *testing.T) {
)
},
wantOut: heredoc.Doc(`
2345678901 tea leaves thwart those who ... 2 files secret about 6 hours ago
3456789012 short desc 11 files secret about 6 hours ago
ID DESCRIPTION FILES VISIBILITY UPDATED
2345678901 tea leaves thwart those ... 2 files secret about 6 hours ago
3456789012 short desc 11 files secret about 6 hours ago
`),
},
{
@ -285,7 +288,10 @@ func Test_listRun(t *testing.T) {
)),
)
},
wantOut: "1234567890 cool.txt 1 file public about 6 hours ago\n",
wantOut: heredoc.Doc(`
ID DESCRIPTION FILES VISIBILITY UPDATED
1234567890 cool.txt 1 file public about 6 hours ago
`),
},
{
name: "nontty output",

View file

@ -7,10 +7,10 @@ import (
"time"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -71,30 +71,20 @@ func listRun(opts *ListOptions) error {
return cmdutil.NewNoResultsError("no GPG keys present in the GitHub account")
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
t := utils.NewTablePrinter(opts.IO)
t := tableprinter.New(opts.IO, tableprinter.WithHeader("EMAIL", "KEY ID", "PUBLIC KEY", "ADDED", "EXPIRES"))
cs := opts.IO.ColorScheme()
now := time.Now()
if t.IsTTY() {
t.AddField("EMAIL", nil, nil)
t.AddField("KEY ID", nil, nil)
t.AddField("PUBLIC KEY", nil, nil)
t.AddField("ADDED", nil, nil)
t.AddField("EXPIRES", nil, nil)
t.EndRow()
}
for _, gpgKey := range gpgKeys {
t.AddField(gpgKey.Emails.String(), nil, nil)
t.AddField(gpgKey.KeyID, nil, nil)
t.AddField(gpgKey.PublicKey, truncateMiddle, nil)
t.AddField(gpgKey.Emails.String())
t.AddField(gpgKey.KeyID)
t.AddField(gpgKey.PublicKey, tableprinter.WithTruncate(truncateMiddle))
createdAt := gpgKey.CreatedAt.Format(time.RFC3339)
if t.IsTTY() {
createdAt = text.FuzzyAgoAbbr(now, gpgKey.CreatedAt)
}
t.AddField(createdAt, nil, cs.Gray)
t.AddField(createdAt, tableprinter.WithColor(cs.Gray))
expiresAt := gpgKey.ExpiresAt.Format(time.RFC3339)
if t.IsTTY() {
@ -104,7 +94,7 @@ func listRun(opts *ListOptions) error {
expiresAt = gpgKey.ExpiresAt.Format("2006-01-02")
}
}
t.AddField(expiresAt, nil, cs.Gray)
t.AddField(expiresAt, tableprinter.WithColor(cs.Gray))
t.EndRow()
}

View file

@ -199,7 +199,7 @@ func developRunList(opts *DevelopOptions, apiClient *api.Client, issueRepo ghrep
func printLinkedBranches(io *iostreams.IOStreams, branches []api.LinkedBranch) {
cs := io.ColorScheme()
table := tableprinter.New(io)
table := tableprinter.New(io, tableprinter.WithHeader("BRANCH", "URL"))
for _, branch := range branches {
table.AddField(branch.BranchName, tableprinter.WithColor(cs.ColorFromString("cyan")))
table.AddField(branch.URL)

View file

@ -6,6 +6,7 @@ import (
"net/http"
"testing"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/context"
"github.com/cli/cli/v2/git"
"github.com/cli/cli/v2/internal/ghrepo"
@ -245,7 +246,14 @@ func TestDevelopRun(t *testing.T) {
assert.Equal(t, "REPO", inputs["name"])
}))
},
expectedOut: "\nShowing linked branches for OWNER/REPO#42\n\nfoo https://github.com/OWNER/REPO/tree/foo\nbar https://github.com/OWNER/OTHER-REPO/tree/bar\n",
expectedOut: heredoc.Doc(`
Showing linked branches for OWNER/REPO#42
BRANCH URL
foo https://github.com/OWNER/REPO/tree/foo
bar https://github.com/OWNER/OTHER-REPO/tree/bar
`),
},
{
name: "list branches for an issue providing an issue url",

View file

@ -104,9 +104,10 @@ func TestIssueList_tty(t *testing.T) {
Showing 3 of 3 open issues in OWNER/REPO
#1 number won label about 1 day ago
#2 number too label about 1 month ago
#4 number fore label about 2 years ago
ID TITLE LABELS UPDATED
#1 number won label about 1 day ago
#2 number too label about 1 month ago
#4 number fore label about 2 years ago
`), output.String())
assert.Equal(t, ``, output.Stderr())
}

View file

@ -7,33 +7,38 @@ import (
"time"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
)
func PrintIssues(io *iostreams.IOStreams, now time.Time, prefix string, totalCount int, issues []api.Issue) {
cs := io.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
table := utils.NewTablePrinter(io)
isTTY := io.IsStdoutTTY()
headers := []string{"ID"}
if !isTTY {
headers = append(headers, "STATE")
}
headers = append(headers,
"TITLE",
"LABELS",
"UPDATED",
)
table := tableprinter.New(io, tableprinter.WithHeader(headers...))
for _, issue := range issues {
issueNum := strconv.Itoa(issue.Number)
if table.IsTTY() {
if isTTY {
issueNum = "#" + issueNum
}
issueNum = prefix + issueNum
table.AddField(issueNum, nil, cs.ColorFromString(prShared.ColorForIssueState(issue)))
if !table.IsTTY() {
table.AddField(issue.State, nil, nil)
}
table.AddField(text.RemoveExcessiveWhitespace(issue.Title), nil, nil)
table.AddField(issueLabelList(&issue, cs, table.IsTTY()), nil, nil)
if table.IsTTY() {
table.AddField(text.FuzzyAgo(now, issue.UpdatedAt), nil, cs.Gray)
} else {
table.AddField(issue.UpdatedAt.String(), nil, nil)
table.AddField(issueNum, tableprinter.WithColor(cs.ColorFromString(prShared.ColorForIssueState(issue))))
if !isTTY {
table.AddField(issue.State)
}
table.AddField(text.RemoveExcessiveWhitespace(issue.Title))
table.AddField(issueLabelList(&issue, cs, isTTY))
table.AddTimeField(now, issue.UpdatedAt, cs.Gray)
table.EndRow()
}
_ = table.Render()

View file

@ -7,10 +7,10 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -134,13 +134,12 @@ func listRun(opts *listOptions) error {
func printLabels(io *iostreams.IOStreams, labels []label) error {
cs := io.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
table := utils.NewTablePrinter(io)
table := tableprinter.New(io, tableprinter.WithHeader("NAME", "DESCRIPTION", "COLOR"))
for _, label := range labels {
table.AddField(label.Name, nil, cs.ColorFromRGB(label.Color))
table.AddField(label.Description, text.Truncate, nil)
table.AddField("#"+label.Color, nil, nil)
table.AddField(label.Name, tableprinter.WithColor(cs.ColorFromRGB(label.Color)))
table.AddField(label.Description)
table.AddField("#" + label.Color)
table.EndRow()
}

View file

@ -215,7 +215,14 @@ func TestListRun(t *testing.T) {
),
)
},
wantStdout: "\nShowing 2 of 2 labels in OWNER/REPO\n\nbug This is a bug label #d73a4a\ndocs This is a docs label #ffa8da\n",
wantStdout: heredoc.Doc(`
Showing 2 of 2 labels in OWNER/REPO
NAME DESCRIPTION COLOR
bug This is a bug label #d73a4a
docs This is a docs label #ffa8da
`),
},
{
name: "lists labels notty",
@ -343,6 +350,7 @@ func TestListRun(t *testing.T) {
Showing 2 of 2 labels in OWNER/REPO
NAME DESCRIPTION COLOR
bug This is a bug label #d73a4a
docs This is a docs label #ffa8da
`),

View file

@ -6,7 +6,6 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
@ -85,16 +84,8 @@ func listRun(opts *ListOptions) error {
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", header)
}
table := tableprinter.New(opts.IO)
for _, org := range listResult.Organizations {
table.AddField(org.Login)
table.EndRow()
}
err = table.Render()
if err != nil {
return err
fmt.Fprintln(opts.IO.Out, org.Login)
}
return nil

View file

@ -7,6 +7,7 @@ import (
"testing"
"time"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/browser"
fd "github.com/cli/cli/v2/internal/featuredetection"
@ -167,7 +168,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/someFailing.json"),
)
},
wantOut: "Some checks were not successful\n0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX sad tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n* slow tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
Some checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
* slow tests 1m26s sweet link
`),
wantErr: "SilentError",
},
{
@ -179,7 +188,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/someCancelled.json"),
)
},
wantOut: "Some checks were cancelled\n1 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n- sad tests 1m26s sweet link\n✓ awesome tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
Some checks were cancelled
1 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
- sad tests 1m26s sweet link
awesome tests 1m26s sweet link
`),
wantErr: "",
},
{
@ -191,7 +208,16 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/somePending.json"),
)
},
wantOut: "Some checks are still pending\n1 cancelled, 0 failing, 2 successful, 0 skipped, and 1 pending checks\n\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n* slow tests 1m26s sweet link\n- sad tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
Some checks are still pending
1 cancelled, 0 failing, 2 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
rad tests 1m26s sweet link
* slow tests 1m26s sweet link
- sad tests 1m26s sweet link
`),
wantErr: "PendingError",
},
{
@ -203,7 +229,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/allPassing.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests 1m26s sweet link
cool tests 1m26s sweet link
rad tests 1m26s sweet link
`),
wantErr: "",
},
{
@ -216,7 +250,22 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/allPassing.json"),
)
},
wantOut: "\x1b[?1049hAll checks were successful\n0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n\x1b[?1049lAll checks were successful\n0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n",
wantOut: heredoc.Docf(`
%[1]s[?1049hAll checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests 1m26s sweet link
cool tests 1m26s sweet link
rad tests 1m26s sweet link
%[1]s[?1049lAll checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests 1m26s sweet link
cool tests 1m26s sweet link
rad tests 1m26s sweet link
`, "\x1b"),
wantErr: "",
},
{
@ -230,7 +279,24 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/someFailing.json"),
)
},
wantOut: "\x1b[?1049h\x1b[0;0H\x1b[JRefreshing checks status every 0 seconds. Press Ctrl+C to quit.\n\nSome checks were not successful\n0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX sad tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n* slow tests 1m26s sweet link\n\x1b[?1049lSome checks were not successful\n0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks\n\nX sad tests 1m26s sweet link\n✓ cool tests 1m26s sweet link\n* slow tests 1m26s sweet link\n",
wantOut: heredoc.Docf(`
%[1]s[?1049h%[1]s[0;0H%[1]s[JRefreshing checks status every 0 seconds. Press Ctrl+C to quit.
Some checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
* slow tests 1m26s sweet link
%[1]s[?1049lSome checks were not successful
0 cancelled, 1 failing, 1 successful, 0 skipped, and 1 pending checks
NAME DESCRIPTION ELAPSED URL
X sad tests 1m26s sweet link
cool tests 1m26s sweet link
* slow tests 1m26s sweet link
`, "\x1b"),
wantErr: "SilentError",
},
{
@ -242,7 +308,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/withStatuses.json"),
)
},
wantOut: "Some checks were not successful\n0 cancelled, 1 failing, 2 successful, 0 skipped, and 0 pending checks\n\nX a status sweet link\n✓ cool tests 1m26s sweet link\n✓ rad tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
Some checks were not successful
0 cancelled, 1 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
X a status sweet link
cool tests 1m26s sweet link
rad tests 1m26s sweet link
`),
wantErr: "SilentError",
},
{
@ -320,7 +394,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/someSkipping.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 1 successful, 2 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n- rad tests 1m26s sweet link\n- skip tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 2 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
- rad tests 1m26s sweet link
- skip tests 1m26s sweet link
`),
wantErr: "",
},
{
@ -344,7 +426,13 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/onlyRequired.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks\n\n✓ cool tests 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
cool tests 1m26s sweet link
`),
wantErr: "",
},
{
@ -393,7 +481,15 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/withDescriptions.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks\n\n✓ awesome tests awesome description 1m26s sweet link\n✓ cool tests cool description 1m26s sweet link\n✓ rad tests rad description 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 3 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
awesome tests awesome description 1m26s sweet link
cool tests cool description 1m26s sweet link
rad tests rad description 1m26s sweet link
`),
wantErr: "",
},
{
@ -416,7 +512,14 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/withEvents.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks\n\n✓ tests/cool tests (pull_request) cool description 1m26s sweet link\n✓ tests/cool tests (push) cool description 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 2 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
tests/cool tests (pull_request) cool description 1m26s sweet link
tests/cool tests (push) cool description 1m26s sweet link
`),
wantErr: "",
},
{
@ -429,7 +532,13 @@ func Test_checksRun(t *testing.T) {
httpmock.FileResponse("./fixtures/withoutEvents.json"),
)
},
wantOut: "All checks were successful\n0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks\n\n✓ tests/cool tests cool description 1m26s sweet link\n",
wantOut: heredoc.Doc(`
All checks were successful
0 cancelled, 0 failing, 1 successful, 0 skipped, and 0 pending checks
NAME DESCRIPTION ELAPSED URL
tests/cool tests cool description 1m26s sweet link
`),
wantErr: "",
},
{

View file

@ -92,7 +92,14 @@ func printSummary(io *iostreams.IOStreams, counts checkCounts) {
}
func printTable(io *iostreams.IOStreams, checks []check) error {
tp := tableprinter.New(io)
var headers []string
if io.IsStdoutTTY() {
headers = []string{"", "NAME", "DESCRIPTION", "ELAPSED", "URL"}
} else {
headers = []string{"NAME", "STATUS", "ELAPSED", "URL", "DESCRIPTION"}
}
tp := tableprinter.New(io, tableprinter.WithHeader(headers...))
sort.Slice(checks, func(i, j int) bool {
b0 := checks[i].Bucket

View file

@ -11,11 +11,11 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/pr/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -199,24 +199,35 @@ func listRun(opts *ListOptions) error {
}
cs := opts.IO.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
table := utils.NewTablePrinter(opts.IO)
isTTY := opts.IO.IsStdoutTTY()
headers := []string{
"ID",
"TITLE",
"BRANCH",
}
if !isTTY {
headers = append(headers, "STATE")
}
headers = append(headers, "CREATED AT")
table := tableprinter.New(opts.IO, tableprinter.WithHeader(headers...))
for _, pr := range listResult.PullRequests {
prNum := strconv.Itoa(pr.Number)
if table.IsTTY() {
if isTTY {
prNum = "#" + prNum
}
table.AddField(prNum, nil, cs.ColorFromString(shared.ColorForPRState(pr)))
table.AddField(text.RemoveExcessiveWhitespace(pr.Title), nil, nil)
table.AddField(pr.HeadLabel(), nil, cs.Cyan)
if !table.IsTTY() {
table.AddField(prStateWithDraft(&pr), nil, nil)
table.AddField(prNum, tableprinter.WithColor(cs.ColorFromString(shared.ColorForPRState(pr))))
table.AddField(text.RemoveExcessiveWhitespace(pr.Title))
table.AddField(pr.HeadLabel(), tableprinter.WithColor(cs.Cyan))
if !isTTY {
table.AddField(prStateWithDraft(&pr))
}
if table.IsTTY() {
table.AddField(text.FuzzyAgo(opts.Now(), pr.CreatedAt), nil, cs.Gray)
if isTTY {
table.AddField(text.FuzzyAgo(opts.Now(), pr.CreatedAt), tableprinter.WithColor(cs.Gray))
} else {
table.AddField(pr.CreatedAt.String(), nil, nil)
table.AddField(pr.CreatedAt.String())
}
table.EndRow()
}

View file

@ -84,6 +84,7 @@ func TestPRList(t *testing.T) {
Showing 3 of 3 open pull requests in OWNER/REPO
ID TITLE BRANCH CREATED AT
#32 New feature feature about 3 hours ago
#29 Fixed bad bug hubot:bug-fix about 1 month ago
#28 Improve documentation docs about 2 years ago

View file

@ -52,7 +52,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(config listConfig) error) *cobra.C
opts.number = int32(num)
}
t := tableprinter.New(f.IOStreams)
t := tableprinter.New(f.IOStreams, tableprinter.WithHeader("Name", "Data type", "ID"))
config := listConfig{
io: f.IOStreams,
tp: t,
@ -109,8 +109,6 @@ func printResults(config listConfig, fields []queries.ProjectField, login string
return cmdutil.NewNoResultsError(fmt.Sprintf("Project %d for owner %s has no fields", config.opts.number, login))
}
config.tp.HeaderRow("Name", "Data type", "ID")
for _, f := range fields {
config.tp.AddField(f.Name())
config.tp.AddField(f.Type())

View file

@ -163,7 +163,8 @@ func TestRunList_User(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "monalisa",
@ -256,7 +257,8 @@ func TestRunList_Org(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "github",
@ -339,7 +341,8 @@ func TestRunList_Me(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "@me",
@ -406,7 +409,8 @@ func TestRunList_Empty(t *testing.T) {
ios, _, _, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "@me",

View file

@ -52,7 +52,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(config listConfig) error) *cobra.C
opts.number = int32(num)
}
t := tableprinter.New(f.IOStreams)
t := tableprinter.New(f.IOStreams, tableprinter.WithHeader("Type", "Title", "Number", "Repository", "ID"))
config := listConfig{
io: f.IOStreams,
tp: t,
@ -108,8 +108,6 @@ func printResults(config listConfig, items []queries.ProjectItem, login string)
return cmdutil.NewNoResultsError(fmt.Sprintf("Project %d for owner %s has no items", config.opts.number, login))
}
config.tp.HeaderRow("Type", "Title", "Number", "Repository", "ID")
for _, i := range items {
config.tp.AddField(i.Type())
config.tp.AddField(i.Title())

View file

@ -178,7 +178,8 @@ func TestRunList_User(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "monalisa",
@ -285,7 +286,8 @@ func TestRunList_Org(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "github",
@ -382,7 +384,8 @@ func TestRunList_Me(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
number: 1,
owner: "@me",

View file

@ -52,7 +52,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(config listConfig) error) *cobra.C
URLOpener := func(url string) error {
return f.Browser.Browse(url)
}
t := tableprinter.New(f.IOStreams)
t := tableprinter.New(f.IOStreams, tableprinter.WithHeader("Number", "Title", "State", "ID"))
config := listConfig{
tp: t,
client: client,
@ -157,8 +157,6 @@ func printResults(config listConfig, projects []queries.Project, owner string) e
return cmdutil.NewNoResultsError(fmt.Sprintf("No projects found for %s", owner))
}
config.tp.HeaderRow("Number", "Title", "State", "ID")
for _, p := range projects {
config.tp.AddField(strconv.Itoa(int(p.Number)), tableprinter.WithTruncate(nil))
config.tp.AddField(p.Title)

View file

@ -154,7 +154,8 @@ func TestRunList(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
owner: "monalisa",
},
@ -225,7 +226,8 @@ func TestRunList_Me(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
owner: "@me",
},
@ -296,7 +298,8 @@ func TestRunListViewer(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{},
client: client,
io: ios,
@ -374,7 +377,8 @@ func TestRunListOrg(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
owner: "github",
},
@ -428,7 +432,8 @@ func TestRunListEmpty(t *testing.T) {
ios, _, _, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{},
client: client,
io: ios,
@ -504,7 +509,8 @@ func TestRunListWithClosed(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
config := listConfig{
tp: tableprinter.New(ios),
//nolint:staticcheck // SA1019: tableprinter.NoHeaders temporarily allowed in project tests.
tp: tableprinter.New(ios, tableprinter.NoHeader),
opts: listOpts{
owner: "monalisa",
closed: true,

View file

@ -78,9 +78,8 @@ func listRun(opts *ListOptions) error {
fmt.Fprintf(opts.IO.ErrOut, "failed to start pager: %v\n", err)
}
table := tableprinter.New(opts.IO)
table := tableprinter.New(opts.IO, tableprinter.WithHeader("Title", "Type", "Tag name", "Published"))
iofmt := opts.IO.ColorScheme()
table.HeaderRow("Title", "Type", "Tag name", "Published")
for _, rel := range releases {
title := text.RemoveExcessiveWhitespace(rel.Name)
if title == "" {

View file

@ -10,12 +10,12 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/release/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/markdown"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -154,11 +154,11 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
if len(release.Assets) > 0 {
fmt.Fprintf(w, "%s\n", iofmt.Bold("Assets"))
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
table := utils.NewTablePrinter(io)
//nolint:staticcheck // SA1019: Showing NAME|SIZE headers adds nothing to table.
table := tableprinter.New(io, tableprinter.NoHeader)
for _, a := range release.Assets {
table.AddField(a.Name, nil, nil)
table.AddField(humanFileSize(a.Size), nil, nil)
table.AddField(a.Name)
table.AddField(humanFileSize(a.Size))
table.EndRow()
}
err := table.Render()

View file

@ -7,10 +7,10 @@ import (
"time"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -77,28 +77,28 @@ func listRun(opts *ListOptions) error {
return opts.Exporter.Write(opts.IO, deployKeys)
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
t := utils.NewTablePrinter(opts.IO)
t := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "TITLE", "TYPE", "KEY", "CREATED AT"))
cs := opts.IO.ColorScheme()
now := time.Now()
for _, deployKey := range deployKeys {
sshID := strconv.Itoa(deployKey.ID)
t.AddField(sshID, nil, nil)
t.AddField(deployKey.Title, nil, nil)
t.AddField(sshID)
t.AddField(deployKey.Title)
sshType := "read-only"
if !deployKey.ReadOnly {
sshType = "read-write"
}
t.AddField(sshType, nil, nil)
t.AddField(deployKey.Key, truncateMiddle, nil)
t.AddField(sshType)
t.AddField(deployKey.Key, tableprinter.WithTruncate(truncateMiddle))
// TODO: Modify AddTimeField, add AddAbbrTimeField, or something else.
createdAt := deployKey.CreatedAt.Format(time.RFC3339)
if t.IsTTY() {
createdAt = text.FuzzyAgoAbbr(now, deployKey.CreatedAt)
}
t.AddField(createdAt, nil, cs.Gray)
t.AddField(createdAt, tableprinter.WithColor(cs.Gray))
t.EndRow()
}

View file

@ -48,6 +48,7 @@ func TestListRun(t *testing.T) {
)
},
wantStdout: heredoc.Doc(`
ID TITLE TYPE KEY CREATED AT
1234 Mac read-only ssh-rsa AAAABbBB123 1d
5678 hubot@Windows read-write ssh-rsa EEEEEEEK247 1d
`),

View file

@ -12,10 +12,10 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/config"
fd "github.com/cli/cli/v2/internal/featuredetection"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
)
type ListOptions struct {
@ -176,9 +176,9 @@ func listRun(opts *ListOptions) error {
}
cs := opts.IO.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(opts.IO)
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("NAME", "DESCRIPTION", "INFO", "UPDATED"))
totalMatchCount := len(listResult.Repositories)
for _, repo := range listResult.Repositories {
info := repoInfo(repo)
infoColor := cs.Gray
@ -192,13 +192,13 @@ func listRun(opts *ListOptions) error {
t = &repo.CreatedAt
}
tp.AddField(repo.NameWithOwner, nil, cs.Bold)
tp.AddField(text.RemoveExcessiveWhitespace(repo.Description), nil, nil)
tp.AddField(info, nil, infoColor)
tp.AddField(repo.NameWithOwner, tableprinter.WithColor(cs.Bold))
tp.AddField(text.RemoveExcessiveWhitespace(repo.Description))
tp.AddField(info, tableprinter.WithColor(infoColor))
if tp.IsTTY() {
tp.AddField(text.FuzzyAgoAbbr(opts.Now(), *t), nil, cs.Gray)
tp.AddField(text.FuzzyAgoAbbr(opts.Now(), *t), tableprinter.WithColor(cs.Gray))
} else {
tp.AddField(t.Format(time.RFC3339), nil, nil)
tp.AddField(t.Format(time.RFC3339))
}
tp.EndRow()
}
@ -208,11 +208,15 @@ func listRun(opts *ListOptions) error {
}
if opts.IO.IsStdoutTTY() {
hasFilters := filter.Visibility != "" || filter.Fork || filter.Source || filter.Language != "" || len(filter.Topic) > 0
title := listHeader(listResult.Owner, len(listResult.Repositories), listResult.TotalCount, hasFilters)
title := listHeader(listResult.Owner, totalMatchCount, listResult.TotalCount, hasFilters)
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title)
}
return tp.Render()
if totalMatchCount > 0 {
return tp.Render()
}
return nil
}
func listHeader(owner string, matchCount, totalMatchCount int, hasFilters bool) string {

View file

@ -385,6 +385,7 @@ func TestRepoList_tty(t *testing.T) {
Showing 3 of 3 repositories in @octocat
NAME DESCRIPTION INFO UPDATED
octocat/hello-world My first repository public 8h
octocat/cli GitHub CLI public, fork 8h
octocat/testing private 7d

View file

@ -161,8 +161,7 @@ func listRun(opts *ListOptions) error {
fmt.Fprintf(opts.IO.Out, "\nShowing %d of %d rulesets in %s\n\n", len(result.Rulesets), result.TotalCount, inMsg)
}
tp := tableprinter.New(opts.IO)
tp.HeaderRow("ID", "NAME", "SOURCE", "STATUS", "RULES")
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "NAME", "SOURCE", "STATUS", "RULES"))
for _, rs := range result.Rulesets {
tp.AddField(strconv.Itoa(rs.DatabaseId), tableprinter.WithColor(cs.Cyan))

View file

@ -7,12 +7,12 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/run/shared"
workflowShared "github.com/cli/cli/v2/pkg/cmd/workflow/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -138,41 +138,28 @@ func listRun(opts *ListOptions) error {
return opts.Exporter.Write(opts.IO, runs)
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(opts.IO)
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("STATUS", "TITLE", "WORKFLOW", "BRANCH", "EVENT", "ID", "ELAPSED", "AGE"))
cs := opts.IO.ColorScheme()
if tp.IsTTY() {
tp.AddField("STATUS", nil, nil)
tp.AddField("TITLE", nil, nil)
tp.AddField("WORKFLOW", nil, nil)
tp.AddField("BRANCH", nil, nil)
tp.AddField("EVENT", nil, nil)
tp.AddField("ID", nil, nil)
tp.AddField("ELAPSED", nil, nil)
tp.AddField("AGE", nil, nil)
tp.EndRow()
}
for _, run := range runs {
if tp.IsTTY() {
symbol, symbolColor := shared.Symbol(cs, run.Status, run.Conclusion)
tp.AddField(symbol, nil, symbolColor)
tp.AddField(symbol, tableprinter.WithColor(symbolColor))
} else {
tp.AddField(string(run.Status), nil, nil)
tp.AddField(string(run.Conclusion), nil, nil)
tp.AddField(string(run.Status))
tp.AddField(string(run.Conclusion))
}
tp.AddField(run.Title(), nil, cs.Bold)
tp.AddField(run.Title(), tableprinter.WithColor(cs.Bold))
tp.AddField(run.WorkflowName(), nil, nil)
tp.AddField(run.HeadBranch, nil, cs.Bold)
tp.AddField(string(run.Event), nil, nil)
tp.AddField(fmt.Sprintf("%d", run.ID), nil, cs.Cyan)
tp.AddField(run.WorkflowName())
tp.AddField(run.HeadBranch, tableprinter.WithColor(cs.Bold))
tp.AddField(string(run.Event))
tp.AddField(fmt.Sprintf("%d", run.ID), tableprinter.WithColor(cs.Cyan))
tp.AddField(run.Duration(opts.now).String(), nil, nil)
tp.AddField(text.FuzzyAgoAbbr(time.Now(), run.StartedTime()), nil, nil)
tp.AddField(run.Duration(opts.now).String())
tp.AddField(text.FuzzyAgoAbbr(time.Now(), run.StartedTime()))
tp.EndRow()
}

View file

@ -155,8 +155,7 @@ func displayResults(io *iostreams.IOStreams, now time.Time, results search.Commi
now = time.Now()
}
cs := io.ColorScheme()
tp := tableprinter.New(io)
tp.HeaderRow("Repo", "SHA", "Message", "Author", "Created")
tp := tableprinter.New(io, tableprinter.WithHeader("Repo", "SHA", "Message", "Author", "Created"))
for _, commit := range results.Items {
tp.AddField(commit.Repo.FullName)
tp.AddField(commit.Sha)

View file

@ -158,8 +158,7 @@ func displayResults(io *iostreams.IOStreams, now time.Time, results search.Repos
now = time.Now()
}
cs := io.ColorScheme()
tp := tableprinter.New(io)
tp.HeaderRow("Name", "Description", "Visibility", "Updated")
tp := tableprinter.New(io, tableprinter.WithHeader("Name", "Description", "Visibility", "Updated"))
for _, repo := range results.Items {
tags := []string{visibilityLabel(repo)}
if repo.IsFork {

View file

@ -95,14 +95,16 @@ func displayIssueResults(io *iostreams.IOStreams, now time.Time, et EntityType,
if now.IsZero() {
now = time.Now()
}
isTTY := io.IsStdoutTTY()
cs := io.ColorScheme()
tp := tableprinter.New(io)
var headers []string
if et == Both {
tp.HeaderRow("Kind", "Repo", "ID", "Title", "Labels", "Updated")
headers = []string{"Kind", "Repo", "ID", "Title", "Labels", "Updated"}
} else {
tp.HeaderRow("Repo", "ID", "Title", "Labels", "Updated")
headers = []string{"Repo", "ID", "Title", "Labels", "Updated"}
}
cs := io.ColorScheme()
tp := tableprinter.New(io, tableprinter.WithHeader(headers...))
for _, issue := range results.Items {
if et == Both {
kind := "issue"
@ -115,7 +117,7 @@ func displayIssueResults(io *iostreams.IOStreams, now time.Time, et EntityType,
name := comp[len(comp)-2:]
tp.AddField(strings.Join(name, "/"))
issueNum := strconv.Itoa(issue.Number)
if isTTY {
if tp.IsTTY() {
issueNum = "#" + issueNum
}
if issue.IsPullRequest() {
@ -125,16 +127,16 @@ func displayIssueResults(io *iostreams.IOStreams, now time.Time, et EntityType,
color := tableprinter.WithColor(cs.ColorFromString(colorForIssueState(issue.State(), issue.StateReason)))
tp.AddField(issueNum, color)
}
if !isTTY {
if !tp.IsTTY() {
tp.AddField(issue.State())
}
tp.AddField(text.RemoveExcessiveWhitespace(issue.Title))
tp.AddField(listIssueLabels(&issue, cs, isTTY))
tp.AddField(listIssueLabels(&issue, cs, tp.IsTTY()))
tp.AddTimeField(now, issue.UpdatedAt, cs.Gray)
tp.EndRow()
}
if isTTY {
if tp.IsTTY() {
var header string
switch et {
case Both:

View file

@ -158,12 +158,14 @@ func listRun(opts *ListOptions) error {
return opts.Exporter.Write(opts.IO, secrets)
}
table := tableprinter.New(opts.IO)
var headers []string
if secretEntity == shared.Organization || secretEntity == shared.User {
table.HeaderRow("Name", "Updated", "Visibility")
headers = []string{"Name", "Updated", "Visibility"}
} else {
table.HeaderRow("Name", "Updated")
headers = []string{"Name", "Updated"}
}
table := tableprinter.New(opts.IO, tableprinter.WithHeader(headers...))
for _, secret := range secrets {
table.AddField(secret.Name)
table.AddTimeField(opts.Now(), secret.UpdatedAt, nil)

View file

@ -10,11 +10,11 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
"github.com/cli/cli/v2/pkg/cmd/ssh-key/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -79,36 +79,26 @@ func listRun(opts *ListOptions) error {
return cmdutil.NewNoResultsError("no SSH keys present in the GitHub account")
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
t := utils.NewTablePrinter(opts.IO)
t := tableprinter.New(opts.IO, tableprinter.WithHeader("TITLE", "ID", "KEY", "TYPE", "ADDED"))
cs := opts.IO.ColorScheme()
now := time.Now()
if t.IsTTY() {
t.AddField("TITLE", nil, nil)
t.AddField("ID", nil, nil)
t.AddField("KEY", nil, nil)
t.AddField("TYPE", nil, nil)
t.AddField("ADDED", nil, nil)
t.EndRow()
}
for _, sshKey := range sshKeys {
id := strconv.Itoa(sshKey.ID)
createdAt := sshKey.CreatedAt.Format(time.RFC3339)
if t.IsTTY() {
t.AddField(sshKey.Title, nil, nil)
t.AddField(id, nil, nil)
t.AddField(sshKey.Key, truncateMiddle, nil)
t.AddField(sshKey.Type, nil, nil)
t.AddField(text.FuzzyAgoAbbr(now, sshKey.CreatedAt), nil, cs.Gray)
t.AddField(sshKey.Title)
t.AddField(id)
t.AddField(sshKey.Key, tableprinter.WithTruncate(truncateMiddle))
t.AddField(sshKey.Type)
t.AddField(text.FuzzyAgoAbbr(now, sshKey.CreatedAt), tableprinter.WithColor(cs.Gray))
} else {
t.AddField(sshKey.Title, nil, nil)
t.AddField(sshKey.Key, nil, nil)
t.AddField(createdAt, nil, nil)
t.AddField(id, nil, nil)
t.AddField(sshKey.Type, nil, nil)
t.AddField(sshKey.Title)
t.AddField(sshKey.Key)
t.AddField(createdAt)
t.AddField(id)
t.AddField(sshKey.Type)
}
t.EndRow()

View file

@ -15,11 +15,11 @@ import (
"github.com/MakeNowJust/heredoc"
"github.com/charmbracelet/lipgloss"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/pkg/cmd/factory"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/set"
"github.com/cli/cli/v2/utils"
ghAPI "github.com/cli/go-gh/v2/pkg/api"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
@ -677,32 +677,27 @@ func statusRun(opts *StatusOptions) error {
section := func(header string, items []StatusItem, width, rowLimit int) (string, error) {
tableOut := &bytes.Buffer{}
fmt.Fprintln(tableOut, cs.Bold(header))
//nolint:staticcheck // SA1019: utils.NewTablePrinterWithOptions is deprecated: use internal/tableprinter
tp := utils.NewTablePrinterWithOptions(opts.IO, utils.TablePrinterOptions{
IsTTY: opts.IO.IsStdoutTTY(),
MaxWidth: width,
Out: tableOut,
})
if len(items) == 0 {
tp.AddField("Nothing here ^_^", nil, nil)
tp.EndRow()
fmt.Fprintln(tableOut, "Nothing here ^_^")
} else {
//nolint:staticcheck // SA1019: Headers distract from numerous nested tables.
tp := tableprinter.NewWithWriter(tableOut, opts.IO.IsStdoutTTY(), width, cs, tableprinter.NoHeader)
for i, si := range items {
if i == rowLimit {
break
}
tp.AddField(si.Identifier, nil, idStyle)
tp.AddField(si.Identifier, tableprinter.WithColor(idStyle), tableprinter.WithTruncate(nil))
if si.Reason != "" {
tp.AddField(si.Reason, nil, nil)
tp.AddField(si.Reason)
}
tp.AddField(si.Preview(), nil, nil)
tp.AddField(si.Preview())
tp.EndRow()
}
}
err := tp.Render()
if err != nil {
return "", err
err := tp.Render()
if err != nil {
return "", err
}
}
return tableOut.String(), nil

View file

@ -124,12 +124,14 @@ func listRun(opts *ListOptions) error {
fmt.Fprintf(opts.IO.ErrOut, "failed to start pager: %v\n", err)
}
table := tableprinter.New(opts.IO)
var headers []string
if variableEntity == shared.Organization {
table.HeaderRow("Name", "Value", "Updated", "Visibility")
headers = []string{"Name", "Value", "Updated", "Visibility"}
} else {
table.HeaderRow("Name", "Value", "Updated")
headers = []string{"Name", "Value", "Updated"}
}
table := tableprinter.New(opts.IO, tableprinter.WithHeader(headers...))
for _, variable := range variables {
table.AddField(variable.Name)
table.AddField(variable.Value)

View file

@ -111,8 +111,7 @@ func listRun(opts *ListOptions) error {
}
cs := opts.IO.ColorScheme()
tp := tableprinter.New(opts.IO)
tp.HeaderRow("Name", "State", "ID")
tp := tableprinter.New(opts.IO, tableprinter.WithHeader("Name", "State", "ID"))
for _, workflow := range filteredWorkflows {
tp.AddField(workflow.Name)

View file

@ -11,13 +11,13 @@ import (
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/internal/tableprinter"
"github.com/cli/cli/v2/internal/text"
runShared "github.com/cli/cli/v2/pkg/cmd/run/shared"
"github.com/cli/cli/v2/pkg/cmd/workflow/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/cli/v2/pkg/markdown"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
@ -213,8 +213,6 @@ func viewWorkflowInfo(opts *ViewOptions, client *api.Client, repo ghrepo.Interfa
out := opts.IO.Out
cs := opts.IO.ColorScheme()
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(opts.IO)
// Header
filename := workflow.Base()
@ -228,26 +226,39 @@ func viewWorkflowInfo(opts *ViewOptions, client *api.Client, repo ghrepo.Interfa
fmt.Fprintln(out, "Recent runs")
}
headers := make([]string, 0, 8)
if opts.Raw {
headers = append(headers, "STATUS", "CONCLUSION")
} else {
headers = append(headers, "")
}
headers = append(headers, "TITLE", "WORKFLOW", "BRANCH", "EVENT")
if opts.Raw {
headers = append(headers, "ELAPSED")
}
headers = append(headers, "ID")
tp := tableprinter.New(opts.IO, tableprinter.WithHeader(headers...))
for _, run := range wr.WorkflowRuns {
if opts.Raw {
tp.AddField(string(run.Status), nil, nil)
tp.AddField(string(run.Conclusion), nil, nil)
tp.AddField(string(run.Status))
tp.AddField(string(run.Conclusion))
} else {
symbol, symbolColor := runShared.Symbol(cs, run.Status, run.Conclusion)
tp.AddField(symbol, nil, symbolColor)
tp.AddField(symbol, tableprinter.WithColor(symbolColor))
}
tp.AddField(run.Title(), nil, cs.Bold)
tp.AddField(run.Title(), tableprinter.WithColor(cs.Bold))
tp.AddField(run.WorkflowName(), nil, nil)
tp.AddField(run.HeadBranch, nil, cs.Bold)
tp.AddField(string(run.Event), nil, nil)
tp.AddField(run.WorkflowName())
tp.AddField(run.HeadBranch, tableprinter.WithColor(cs.Bold))
tp.AddField(string(run.Event))
if opts.Raw {
tp.AddField(run.Duration(opts.now).String(), nil, nil)
tp.AddField(run.Duration(opts.now).String())
}
tp.AddField(fmt.Sprintf("%d", run.ID), nil, cs.Cyan)
tp.AddField(fmt.Sprintf("%d", run.ID), tableprinter.WithColor(cs.Cyan))
tp.EndRow()
}

View file

@ -176,10 +176,11 @@ func TestViewRun(t *testing.T) {
Total runs 10
Recent runs
X cool commit a workflow trunk push 1
* cool commit a workflow trunk push 2
cool commit a workflow trunk push 3
X cool commit a workflow trunk push 4
TITLE WORKFLOW BRANCH EVENT ID
X cool commit a workflow trunk push 1
* cool commit a workflow trunk push 2
cool commit a workflow trunk push 3
X cool commit a workflow trunk push 4
To see more runs for this workflow, try: gh run list --workflow flow.yml
To see the YAML for this workflow, try: gh workflow view flow.yml --yaml

View file

@ -9,16 +9,17 @@ import (
)
var (
magenta = ansi.ColorFunc("magenta")
cyan = ansi.ColorFunc("cyan")
red = ansi.ColorFunc("red")
yellow = ansi.ColorFunc("yellow")
blue = ansi.ColorFunc("blue")
green = ansi.ColorFunc("green")
gray = ansi.ColorFunc("black+h")
bold = ansi.ColorFunc("default+b")
cyanBold = ansi.ColorFunc("cyan+b")
greenBold = ansi.ColorFunc("green+b")
magenta = ansi.ColorFunc("magenta")
cyan = ansi.ColorFunc("cyan")
red = ansi.ColorFunc("red")
yellow = ansi.ColorFunc("yellow")
blue = ansi.ColorFunc("blue")
green = ansi.ColorFunc("green")
gray = ansi.ColorFunc("black+h")
lightGrayUnderline = ansi.ColorFunc("white+du")
bold = ansi.ColorFunc("default+b")
cyanBold = ansi.ColorFunc("cyan+b")
greenBold = ansi.ColorFunc("green+b")
gray256 = func(t string) string {
return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[m", 38, 242, t)
@ -39,6 +40,10 @@ type ColorScheme struct {
hasTrueColor bool
}
func (c *ColorScheme) Enabled() bool {
return c.enabled
}
func (c *ColorScheme) Bold(t string) string {
if !c.enabled {
return t
@ -104,6 +109,13 @@ func (c *ColorScheme) Grayf(t string, args ...interface{}) string {
return c.Gray(fmt.Sprintf(t, args...))
}
func (c *ColorScheme) LightGrayUnderline(t string) string {
if !c.enabled {
return t
}
return lightGrayUnderline(t)
}
func (c *ColorScheme) Magenta(t string) string {
if !c.enabled {
return t

View file

@ -1,89 +0,0 @@
package utils
import (
"io"
"strings"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/go-gh/v2/pkg/tableprinter"
)
type TablePrinter interface {
IsTTY() bool
AddField(string, func(int, string) string, func(string) string)
EndRow()
Render() error
}
type TablePrinterOptions struct {
IsTTY bool
MaxWidth int
Out io.Writer
}
// Deprecated: use internal/tableprinter
func NewTablePrinter(io *iostreams.IOStreams) TablePrinter {
return NewTablePrinterWithOptions(io, TablePrinterOptions{
IsTTY: io.IsStdoutTTY(),
})
}
// Deprecated: use internal/tableprinter
func NewTablePrinterWithOptions(ios *iostreams.IOStreams, opts TablePrinterOptions) TablePrinter {
var out io.Writer
if opts.Out != nil {
out = opts.Out
} else {
out = ios.Out
}
var maxWidth int
if opts.IsTTY {
if opts.MaxWidth > 0 {
maxWidth = opts.MaxWidth
} else {
maxWidth = ios.TerminalWidth()
}
}
tp := tableprinter.New(out, opts.IsTTY, maxWidth)
return &printer{
tp: tp,
isTTY: opts.IsTTY,
}
}
type printer struct {
tp tableprinter.TablePrinter
colIndex int
isTTY bool
}
func (p printer) IsTTY() bool {
return p.isTTY
}
func (p *printer) AddField(s string, truncateFunc func(int, string) string, colorFunc func(string) string) {
if truncateFunc == nil {
// Disallow ever truncating the 1st column or any URL value
if p.colIndex == 0 || isURL(s) {
p.tp.AddField(s, tableprinter.WithTruncate(nil), tableprinter.WithColor(colorFunc))
} else {
p.tp.AddField(s, tableprinter.WithColor(colorFunc))
}
} else {
p.tp.AddField(s, tableprinter.WithTruncate(truncateFunc), tableprinter.WithColor(colorFunc))
}
p.colIndex++
}
func (p *printer) EndRow() {
p.tp.EndRow()
p.colIndex = 0
}
func (p *printer) Render() error {
return p.tp.Render()
}
func isURL(s string) bool {
return strings.HasPrefix(s, "https://") || strings.HasPrefix(s, "http://")
}