mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-03-14 00:12:42 +00:00
Migrate repo output format to customizable output (#4888)
This commit is contained in:
parent
a1dbcc90a5
commit
9a2b13341e
15 changed files with 316 additions and 85 deletions
|
@ -31,7 +31,7 @@ var userListCmd = &cli.Command{
|
|||
Usage: "list all users",
|
||||
ArgsUsage: " ",
|
||||
Action: userList,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplUserList)},
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplUserList, false)},
|
||||
}
|
||||
|
||||
func userList(ctx context.Context, c *cli.Command) error {
|
||||
|
|
|
@ -31,7 +31,7 @@ var userShowCmd = &cli.Command{
|
|||
Usage: "show user information",
|
||||
ArgsUsage: "<username>",
|
||||
Action: userShow,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplUserInfo)},
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplUserInfo, false)},
|
||||
}
|
||||
|
||||
func userShow(ctx context.Context, c *cli.Command) error {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/shared/logger"
|
||||
|
@ -63,10 +65,15 @@ var GlobalFlags = append([]cli.Flag{
|
|||
|
||||
// FormatFlag return format flag with value set based on template
|
||||
// if hidden value is set, flag will be hidden.
|
||||
func FormatFlag(tmpl string, hidden ...bool) *cli.StringFlag {
|
||||
func FormatFlag(tmpl string, deprecated bool, hidden ...bool) *cli.StringFlag {
|
||||
usage := "format output"
|
||||
if deprecated {
|
||||
usage = fmt.Sprintf("%s (deprecated)", usage)
|
||||
}
|
||||
|
||||
return &cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Usage: usage,
|
||||
Value: tmpl,
|
||||
Hidden: len(hidden) != 0,
|
||||
}
|
||||
|
|
|
@ -52,15 +52,15 @@ func (o *Table) Columns() (cols []string) {
|
|||
|
||||
// AddFieldAlias overrides the field name to allow custom column headers.
|
||||
func (o *Table) AddFieldAlias(field, alias string) *Table {
|
||||
o.fieldAlias[field] = alias
|
||||
o.fieldAlias[strings.ToLower(alias)] = field
|
||||
return o
|
||||
}
|
||||
|
||||
// AddFieldFn adds a function which handles the output of the specified field.
|
||||
func (o *Table) AddFieldFn(field string, fn FieldFn) *Table {
|
||||
o.fieldMapping[field] = fn
|
||||
o.allowedFields[field] = true
|
||||
o.columns[field] = true
|
||||
o.fieldMapping[strings.ToLower(field)] = fn
|
||||
o.allowedFields[strings.ToLower(field)] = true
|
||||
o.columns[strings.ToLower(field)] = true
|
||||
return o
|
||||
}
|
||||
|
||||
|
@ -117,9 +117,6 @@ func (o *Table) ValidateColumns(cols []string) error {
|
|||
func (o *Table) WriteHeader(columns []string) {
|
||||
var header []string
|
||||
for _, col := range columns {
|
||||
if alias, ok := o.fieldAlias[col]; ok {
|
||||
col = alias
|
||||
}
|
||||
header = append(header, strings.ReplaceAll(strings.ToUpper(col), "_", " "))
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.w, strings.Join(header, "\t"))
|
||||
|
@ -146,12 +143,9 @@ func (o *Table) Write(columns []string, obj any) error {
|
|||
for _, col := range columns {
|
||||
colName := strings.ToLower(col)
|
||||
if alias, ok := o.fieldAlias[colName]; ok {
|
||||
if fn, ok := o.fieldMapping[alias]; ok {
|
||||
out = append(out, sanitizeString(fn(obj)))
|
||||
continue
|
||||
}
|
||||
colName = strings.ToLower(alias)
|
||||
}
|
||||
if fn, ok := o.fieldMapping[colName]; ok {
|
||||
if fn, ok := o.fieldMapping[strings.ReplaceAll(colName, "_", "")]; ok {
|
||||
out = append(out, sanitizeString(fn(obj)))
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -32,17 +32,17 @@ func TestTableOutput(t *testing.T) {
|
|||
}
|
||||
})
|
||||
t.Run("AddFieldAlias", func(t *testing.T) {
|
||||
to.AddFieldAlias("woodpecker_ci", "woodpecker ci")
|
||||
if alias, ok := to.fieldAlias["woodpecker_ci"]; !ok || alias != "woodpecker ci" {
|
||||
t.Errorf("woodpecker_ci alias should be 'woodpecker ci', is: %v", alias)
|
||||
to.AddFieldAlias("WoodpeckerCI", "wp")
|
||||
if alias, ok := to.fieldAlias["wp"]; !ok || alias != "WoodpeckerCI" {
|
||||
t.Errorf("'wp' alias should resolve to 'WoodpeckerCI', is: %v", alias)
|
||||
}
|
||||
})
|
||||
t.Run("AddFieldOutputFn", func(t *testing.T) {
|
||||
to.AddFieldFn("woodpecker ci", FieldFn(func(_ any) string {
|
||||
to.AddFieldFn("WoodpeckerCI", FieldFn(func(_ any) string {
|
||||
return "WOODPECKER CI!!!"
|
||||
}))
|
||||
if _, ok := to.fieldMapping["woodpecker ci"]; !ok {
|
||||
t.Errorf("'woodpecker ci' field output fn should be set")
|
||||
if _, ok := to.fieldMapping["woodpeckerci"]; !ok {
|
||||
t.Errorf("'WoodpeckerCI' field output fn should be set")
|
||||
}
|
||||
})
|
||||
t.Run("ValidateColumns", func(t *testing.T) {
|
||||
|
@ -54,14 +54,14 @@ func TestTableOutput(t *testing.T) {
|
|||
}
|
||||
})
|
||||
t.Run("WriteHeader", func(t *testing.T) {
|
||||
to.WriteHeader([]string{"woodpecker_ci", "name"})
|
||||
if wfs.String() != "WOODPECKER CI\tNAME\n" {
|
||||
to.WriteHeader([]string{"wp", "name"})
|
||||
if wfs.String() != "WP\tNAME\n" {
|
||||
t.Errorf("written header should be 'WOODPECKER CI\\tNAME\\n', is: %q", wfs.String())
|
||||
}
|
||||
wfs.Reset()
|
||||
})
|
||||
t.Run("WriteLine", func(t *testing.T) {
|
||||
_ = to.Write([]string{"woodpecker_ci", "name", "number"}, &testFieldsStruct{"test123", 1000000000})
|
||||
_ = to.Write([]string{"wp", "name", "number"}, &testFieldsStruct{"test123", 1000000000})
|
||||
if wfs.String() != "WOODPECKER CI!!!\ttest123\t1000000000\n" {
|
||||
t.Errorf("written line should be 'WOODPECKER CI!!!\\ttest123\\t1000000000\\n', is: %q", wfs.String())
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ var Command = &cli.Command{
|
|||
ArgsUsage: "<repo-id|repo-full-name> <pipeline> <environment>",
|
||||
Action: deploy,
|
||||
Flags: []cli.Flag{
|
||||
common.FormatFlag(tmplDeployInfo),
|
||||
common.FormatFlag(tmplDeployInfo, false),
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
|
|
|
@ -55,13 +55,9 @@ func pipelineOutput(c *cli.Command, pipelines []*woodpecker.Pipeline, fd ...io.W
|
|||
noHeader := c.Bool("output-no-headers")
|
||||
|
||||
var out io.Writer
|
||||
switch len(fd) {
|
||||
case 0:
|
||||
out = os.Stdout
|
||||
case 1:
|
||||
out = os.Stdout
|
||||
if len(fd) > 0 {
|
||||
out = fd[0]
|
||||
default:
|
||||
out = os.Stdout
|
||||
}
|
||||
|
||||
switch outFmt {
|
||||
|
|
|
@ -33,7 +33,7 @@ var pipelinePsCmd = &cli.Command{
|
|||
Usage: "show pipeline steps",
|
||||
ArgsUsage: "<repo-id|repo-full-name> <pipeline>",
|
||||
Action: pipelinePs,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelinePs)},
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelinePs, false)},
|
||||
}
|
||||
|
||||
func pipelinePs(ctx context.Context, c *cli.Command) error {
|
||||
|
|
|
@ -31,7 +31,7 @@ var pipelineQueueCmd = &cli.Command{
|
|||
Usage: "show pipeline queue",
|
||||
ArgsUsage: " ",
|
||||
Action: pipelineQueue,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelineQueue)},
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelineQueue, false)},
|
||||
}
|
||||
|
||||
func pipelineQueue(ctx context.Context, c *cli.Command) error {
|
||||
|
|
|
@ -15,11 +15,19 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/output"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/repo/cron"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/repo/registry"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/repo/secret"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
// Command exports the repository command.
|
||||
|
@ -40,3 +48,84 @@ var Command = &cli.Command{
|
|||
repoUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
func repoOutput(c *cli.Command, repos []*woodpecker.Repo, fd ...io.Writer) error {
|
||||
outFmt, outOpt := output.ParseOutputOptions(c.String("output"))
|
||||
noHeader := c.Bool("output-no-headers")
|
||||
|
||||
legacyFmt := c.String("format")
|
||||
if legacyFmt != "" {
|
||||
log.Warn().Msgf("the --format flag is deprecated, please use --output instead")
|
||||
|
||||
outFmt = "go-template"
|
||||
outOpt = []string{legacyFmt}
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
out = os.Stdout
|
||||
if len(fd) > 0 {
|
||||
out = fd[0]
|
||||
}
|
||||
|
||||
switch outFmt {
|
||||
case "go-template":
|
||||
if len(outOpt) < 1 {
|
||||
return fmt.Errorf("%w: missing template", output.ErrOutputOptionRequired)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(outOpt[0] + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(out, repos); err != nil {
|
||||
return err
|
||||
}
|
||||
case "table":
|
||||
fallthrough
|
||||
default:
|
||||
table := output.NewTable(out)
|
||||
|
||||
// Add custom field mapping for nested Trusted fields
|
||||
table.AddFieldFn("TrustedNetwork", func(obj any) string {
|
||||
repo, ok := obj.(*woodpecker.Repo)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return output.YesNo(repo.Trusted.Network)
|
||||
})
|
||||
table.AddFieldFn("TrustedSecurity", func(obj any) string {
|
||||
repo, ok := obj.(*woodpecker.Repo)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return output.YesNo(repo.Trusted.Security)
|
||||
})
|
||||
table.AddFieldFn("TrustedVolume", func(obj any) string {
|
||||
repo, ok := obj.(*woodpecker.Repo)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return output.YesNo(repo.Trusted.Volumes)
|
||||
})
|
||||
|
||||
table.AddFieldAlias("Is_Active", "Active")
|
||||
table.AddFieldAlias("Is_SCM_Private", "SCM_Private")
|
||||
|
||||
cols := []string{"Full_Name", "Branch", "Forge_URL", "Visibility", "SCM_Private", "Active", "Allow_Pull"}
|
||||
|
||||
if len(outOpt) > 0 {
|
||||
cols = outOpt
|
||||
}
|
||||
if !noHeader {
|
||||
table.WriteHeader(cols)
|
||||
}
|
||||
for _, resource := range repos {
|
||||
if err := table.Write(cols, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@ package repo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
|
@ -30,9 +28,9 @@ var repoListCmd = &cli.Command{
|
|||
Name: "ls",
|
||||
Usage: "list all repos",
|
||||
ArgsUsage: " ",
|
||||
Action: repoList,
|
||||
Flags: []cli.Flag{
|
||||
common.FormatFlag(tmplRepoList),
|
||||
Action: List,
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
common.FormatFlag("", true),
|
||||
&cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "filter by organization",
|
||||
|
@ -41,40 +39,38 @@ var repoListCmd = &cli.Command{
|
|||
Name: "all",
|
||||
Usage: "query all repos, including inactive ones",
|
||||
},
|
||||
},
|
||||
}...),
|
||||
}
|
||||
|
||||
func repoList(ctx context.Context, c *cli.Command) error {
|
||||
func List(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repos, err := repoList(c, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repoOutput(c, repos)
|
||||
}
|
||||
|
||||
func repoList(c *cli.Command, client woodpecker.Client) ([]*woodpecker.Repo, error) {
|
||||
repos := make([]*woodpecker.Repo, 0)
|
||||
opt := woodpecker.RepoListOptions{
|
||||
All: c.Bool("all"),
|
||||
}
|
||||
|
||||
repos, err := client.RepoList(opt)
|
||||
if err != nil || len(repos) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
raw, err := client.RepoList(opt)
|
||||
if err != nil || len(raw) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org := c.String("org")
|
||||
for _, repo := range repos {
|
||||
for _, repo := range raw {
|
||||
if org != "" && org != repo.Owner {
|
||||
continue
|
||||
}
|
||||
if err := tmpl.Execute(os.Stdout, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
return nil
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// Template for repository list items.
|
||||
var tmplRepoList = "\x1b[33m{{ .FullName }}\x1b[0m (id: {{ .ID }}, forgeRemoteID: {{ .ForgeRemoteID }}, isActive: {{ .IsActive }})"
|
||||
|
|
|
@ -16,56 +16,45 @@ package repo
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var repoShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show repository information",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: repoShow,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplRepoInfo)},
|
||||
Action: Show,
|
||||
Flags: common.OutputFlags("table"),
|
||||
}
|
||||
|
||||
func repoShow(ctx context.Context, c *cli.Command) error {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
func Show(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
repo, err := repoShow(c, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return repoOutput(c, []*woodpecker.Repo{repo})
|
||||
}
|
||||
|
||||
func repoShow(c *cli.Command, client woodpecker.Client) (*woodpecker.Repo, error) {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo, err := client.Repo(repoID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, repo)
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
// tTemplate for repo information.
|
||||
var tmplRepoInfo = `Owner: {{ .Owner }}
|
||||
Repo: {{ .Name }}
|
||||
URL: {{ .ForgeURL }}
|
||||
Config path: {{ .Config }}
|
||||
Visibility: {{ .Visibility }}
|
||||
Private: {{ .IsSCMPrivate }}
|
||||
Trusted: {{ .IsTrusted }}
|
||||
Gated: {{ .IsGated }}
|
||||
Require approval for: {{ .RequireApproval }}
|
||||
Clone url: {{ .Clone }}
|
||||
Allow pull-requests: {{ .AllowPullRequests }}
|
||||
`
|
||||
|
|
72
cli/repo/repo_show_test.go
Normal file
72
cli/repo/repo_show_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/woodpecker-go/woodpecker"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/woodpecker-go/woodpecker/mocks"
|
||||
)
|
||||
|
||||
func TestRepoShow(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repoID int64
|
||||
mockRepo *woodpecker.Repo
|
||||
mockError error
|
||||
expectedError bool
|
||||
expected *woodpecker.Repo
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "valid repo by ID",
|
||||
repoID: 123,
|
||||
mockRepo: &woodpecker.Repo{Name: "test-repo"},
|
||||
expected: &woodpecker.Repo{Name: "test-repo"},
|
||||
args: []string{"show", "123"},
|
||||
},
|
||||
{
|
||||
name: "valid repo by full name",
|
||||
repoID: 456,
|
||||
mockRepo: &woodpecker.Repo{ID: 456, Name: "repo", Owner: "owner"},
|
||||
expected: &woodpecker.Repo{ID: 456, Name: "repo", Owner: "owner"},
|
||||
args: []string{"show", "owner/repo"},
|
||||
},
|
||||
{
|
||||
name: "invalid repo ID",
|
||||
repoID: 999,
|
||||
expectedError: true,
|
||||
args: []string{"show", "invalid"},
|
||||
mockError: errors.New("repo not found"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockClient := mocks.NewClient(t)
|
||||
mockClient.On("Repo", tt.repoID).Return(tt.mockRepo, tt.mockError).Maybe()
|
||||
mockClient.On("RepoLookup", "owner/repo").Return(tt.mockRepo, nil).Maybe()
|
||||
|
||||
command := repoShowCmd
|
||||
command.Writer = io.Discard
|
||||
command.Action = func(_ context.Context, c *cli.Command) error {
|
||||
output, err := repoShow(c, mockClient)
|
||||
if tt.expectedError {
|
||||
assert.Error(t, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, output)
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = command.Run(context.Background(), tt.args)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ var repoSyncCmd = &cli.Command{
|
|||
Usage: "synchronize the repository list",
|
||||
ArgsUsage: " ",
|
||||
Action: repoSync,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplRepoList)},
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplRepoList, false)},
|
||||
}
|
||||
|
||||
// TODO: remove this and add an option to the list cmd as we do not store the remote repo list anymore
|
||||
|
@ -66,3 +66,6 @@ func repoSync(ctx context.Context, c *cli.Command) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Template for repository list items.
|
||||
var tmplRepoList = "\x1b[33m{{ .FullName }}\x1b[0m (id: {{ .ID }}, forgeRemoteID: {{ .ForgeRemoteID }}, isActive: {{ .IsActive }})"
|
||||
|
|
85
cli/repo/repo_test.go
Normal file
85
cli/repo/repo_test.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
func TestRepoOutput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "table output with default columns",
|
||||
args: []string{},
|
||||
expected: "FULL NAME BRANCH FORGE URL VISIBILITY SCM PRIVATE ACTIVE ALLOW PULL\norg/repo1 main git.example.com public no yes yes\n",
|
||||
},
|
||||
{
|
||||
name: "table output with custom columns",
|
||||
args: []string{"output", "--output", "table=Name,Forge_URL,Trusted_Network"},
|
||||
expected: "NAME FORGE URL TRUSTED NETWORK\nrepo1 git.example.com yes\n",
|
||||
},
|
||||
{
|
||||
name: "table output with no header",
|
||||
args: []string{"output", "--output-no-headers"},
|
||||
expected: "org/repo1 main git.example.com public no yes yes\n",
|
||||
},
|
||||
{
|
||||
name: "go-template output",
|
||||
args: []string{"output", "--output", "go-template={{range . }}{{.Name}} {{.ForgeURL}} {{.Trusted.Network}}{{end}}"},
|
||||
expected: "repo1 git.example.com true\n",
|
||||
},
|
||||
}
|
||||
|
||||
repos := []*woodpecker.Repo{
|
||||
{
|
||||
Name: "repo1",
|
||||
FullName: "org/repo1",
|
||||
ForgeURL: "git.example.com",
|
||||
Branch: "main",
|
||||
Visibility: "public",
|
||||
IsActive: true,
|
||||
AllowPull: true,
|
||||
Trusted: woodpecker.TrustedConfiguration{
|
||||
Network: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
command := &cli.Command{
|
||||
Writer: io.Discard,
|
||||
Name: "output",
|
||||
Flags: common.OutputFlags("table"),
|
||||
Action: func(_ context.Context, c *cli.Command) error {
|
||||
var buf bytes.Buffer
|
||||
err := repoOutput(c, repos, &buf)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, buf.String())
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_ = command.Run(context.Background(), tt.args)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue