Skip to content

Commit 65466c9

Browse files
authored
Add config command for set/get (#623)
1 parent 27fa719 commit 65466c9

File tree

26 files changed

+876
-82
lines changed

26 files changed

+876
-82
lines changed

cmd/agent/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type ListCmd struct {
3232
Tags []string `help:"Filter agents by tags"`
3333
PerPage int `help:"Number of agents per page" default:"30"`
3434
Limit int `help:"Maximum number of agents to return" default:"100"`
35-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
35+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
3636
}
3737

3838
func (c *ListCmd) Help() string {
@@ -91,7 +91,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
9191
return err
9292
}
9393

94-
format := output.Format(c.Output)
94+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
9595

9696
agents := []buildkite.Agent{}
9797
page := 1
@@ -181,7 +181,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
181181
}
182182
table := output.Table(headers, rows, columnStyles)
183183

184-
writer, cleanup := bkIO.Pager(f.NoPager)
184+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
185185
defer func() {
186186
_ = cleanup()
187187
}()

cmd/agent/view.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
type ViewCmd struct {
2121
Agent string `arg:"" help:"Agent ID to view"`
2222
Web bool `help:"Open agent in a browser" short:"w"`
23-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
23+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
2424
}
2525

2626
func (c *ViewCmd) Help() string {
@@ -58,7 +58,7 @@ func (c *ViewCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
5858

5959
ctx := context.Background()
6060

61-
format := output.Format(c.Output)
61+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
6262

6363
org, id := parseAgentArg(c.Agent, f.Config)
6464

@@ -111,7 +111,7 @@ func (c *ViewCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
111111
"value": "dim",
112112
})
113113

114-
writer, cleanup := bkIO.Pager(f.NoPager)
114+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
115115
defer func() { _ = cleanup() }()
116116

117117
fmt.Fprintf(writer, "Agent %s (%s)\n\n", agentData.Name, agentData.ID)

cmd/artifacts/list.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type ListCmd struct {
2323
BuildNumber string `arg:"" optional:"" help:"Build number to list artifacts for"`
2424
Pipeline string `help:"The pipeline to view. This can be a {pipeline slug} or in the format {org slug}/{pipeline slug}. If omitted, it will be resolved using the current directory." short:"p"`
2525
Job string `help:"List artifacts for a specific job on the given build." short:"j"`
26-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
26+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
2727
}
2828

2929
func (c *ListCmd) Help() string {
@@ -62,10 +62,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
6262
return err
6363
}
6464

65-
format := output.Format(c.Output)
66-
if format != output.FormatJSON && format != output.FormatYAML && format != output.FormatText {
67-
return fmt.Errorf("invalid output format: %s", c.Output)
68-
}
65+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
6966

7067
var args []string
7168
if c.BuildNumber != "" {
@@ -117,7 +114,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
117114
return output.Write(os.Stdout, buildArtifacts, format)
118115
}
119116

120-
writer, cleanup := bkIO.Pager(f.NoPager)
117+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
121118
defer func() { _ = cleanup() }()
122119

123120
if len(buildArtifacts) == 0 {

cmd/build/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type ListCmd struct {
3939
MetaData map[string]string `help:"Filter by build meta-data (key=value format, can be specified multiple times)"`
4040
Limit int `help:"Maximum number of builds to return" default:"50"`
4141
NoLimit bool `help:"Fetch all builds (overrides --limit)"`
42-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
42+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
4343
}
4444

4545
func (c *ListCmd) Help() string {
@@ -140,10 +140,10 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
140140

141141
org := f.Config.OrganizationSlug()
142142

143-
format := output.Format(c.Output)
143+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
144144

145145
if format == output.FormatText {
146-
writer, cleanup := bkIO.Pager(f.NoPager)
146+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
147147
defer func() { _ = cleanup() }()
148148

149149
target := org

cmd/build/view.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ViewCmd struct {
2727
User string `help:"Filter builds to this user. You can use name or email." short:"u" xor:"userfilter"`
2828
Mine bool `help:"Filter builds to only my user." xor:"userfilter"`
2929
Web bool `help:"Open the build in a web browser." short:"w"`
30-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
30+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
3131
}
3232

3333
func (c *ViewCmd) Help() string {
@@ -220,9 +220,9 @@ func (c *ViewCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
220220
},
221221
}
222222

223-
format := output.Format(c.Output)
223+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
224224
if format == output.FormatText {
225-
writer, cleanup := bkIO.Pager(f.NoPager)
225+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
226226
defer func() { _ = cleanup() }()
227227

228228
_, err := fmt.Fprint(writer, buildOutput.TextOutput())

cmd/cluster/list.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
)
2121

2222
type ListCmd struct {
23-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
23+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
2424
}
2525

2626
func (c *ListCmd) Help() string {
@@ -51,10 +51,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
5151
return err
5252
}
5353

54-
format := output.Format(c.Output)
55-
if format != output.FormatJSON && format != output.FormatYAML && format != output.FormatText {
56-
return fmt.Errorf("invalid output format: %s", c.Output)
57-
}
54+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
5855

5956
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
6057
defer stop()
@@ -70,7 +67,7 @@ func (c *ListCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
7067

7168
summary := cluster.ClusterViewTable(clusters...)
7269

73-
writer, cleanup := bkIO.Pager(f.NoPager)
70+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
7471
defer func() { _ = cleanup() }()
7572

7673
fmt.Fprintf(writer, "%v\n", summary)

cmd/cluster/view.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020

2121
type ViewCmd struct {
2222
ClusterID string `arg:"" help:"Cluster ID to view"`
23-
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:"json,yaml,text"`
23+
Output string `help:"Output format. One of: json, yaml, text" short:"o" default:"${output_default_format}" enum:",json,yaml,text"`
2424
}
2525

2626
func (c *ViewCmd) Help() string {
@@ -51,10 +51,7 @@ func (c *ViewCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
5151
return err
5252
}
5353

54-
format := output.Format(c.Output)
55-
if format != output.FormatJSON && format != output.FormatYAML && format != output.FormatText {
56-
return fmt.Errorf("invalid output format: %s", c.Output)
57-
}
54+
format := output.ResolveFormat(c.Output, f.Config.OutputFormat())
5855

5956
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
6057
defer stop()
@@ -79,7 +76,7 @@ func (c *ViewCmd) Run(kongCtx *kong.Context, globals cli.GlobalFlags) error {
7976
return output.Write(os.Stdout, clusterView, format)
8077
}
8178

82-
writer, cleanup := bkIO.Pager(f.NoPager)
79+
writer, cleanup := bkIO.Pager(f.NoPager, f.Config.Pager())
8380
defer func() { _ = cleanup() }()
8481

8582
return output.Write(writer, clusterView, format)

cmd/config/config.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Package config provides commands for managing CLI configuration
2+
package config
3+
4+
import (
5+
"fmt"
6+
"slices"
7+
"strconv"
8+
9+
"github.com/buildkite/cli/v3/internal/config"
10+
)
11+
12+
// ConfigCmd is the root command for managing CLI configuration
13+
type ConfigCmd struct {
14+
List ListCmd `cmd:"" help:"List configuration values." aliases:"ls"`
15+
Get GetCmd `cmd:"" help:"Get a configuration value."`
16+
Set SetCmd `cmd:"" help:"Set a configuration value."`
17+
Unset UnsetCmd `cmd:"" help:"Remove a configuration value."`
18+
}
19+
20+
func (c ConfigCmd) Help() string {
21+
return `Manage CLI configuration settings.
22+
23+
Configuration is stored in two locations:
24+
User config: ~/.config/bk.yaml (global defaults)
25+
Local config: .bk.yaml (repo-specific overrides)
26+
27+
Precedence: Environment variable > Local config > User config > Default
28+
29+
Examples:
30+
$ bk config list # Show all config values
31+
$ bk config get output_format # Get a specific value
32+
$ bk config set output_format yaml # Set default output to YAML
33+
$ bk config set no_pager true --local # Disable pager for this repo
34+
$ bk config unset pager # Reset pager to default`
35+
}
36+
37+
// ConfigKey represents a valid configuration key
38+
type ConfigKey string
39+
40+
const (
41+
KeySelectedOrg ConfigKey = "selected_org"
42+
KeyOutputFormat ConfigKey = "output_format"
43+
KeyNoPager ConfigKey = "no_pager"
44+
KeyQuiet ConfigKey = "quiet"
45+
KeyNoInput ConfigKey = "no_input"
46+
KeyPager ConfigKey = "pager"
47+
)
48+
49+
// AllKeys returns all valid configuration keys
50+
func AllKeys() []ConfigKey {
51+
return []ConfigKey{
52+
KeySelectedOrg,
53+
KeyOutputFormat,
54+
KeyNoPager,
55+
KeyQuiet,
56+
KeyNoInput,
57+
KeyPager,
58+
}
59+
}
60+
61+
// ValidateKey checks if a key is valid
62+
func ValidateKey(key string) (ConfigKey, error) {
63+
k := ConfigKey(key)
64+
if slices.Contains(AllKeys(), k) {
65+
return k, nil
66+
}
67+
return "", fmt.Errorf("unknown config key: %s\nvalid keys: %v", key, AllKeys())
68+
}
69+
70+
// IsLocalOnly returns true if the key can only be set in user config
71+
func (k ConfigKey) IsLocalOnly() bool {
72+
return false
73+
}
74+
75+
// IsUserOnly returns true if the key can only be set in user config
76+
func (k ConfigKey) IsUserOnly() bool {
77+
switch k {
78+
case KeyNoInput, KeyPager:
79+
return true
80+
default:
81+
return false
82+
}
83+
}
84+
85+
// IsBool returns true if the key is a boolean value
86+
func (k ConfigKey) IsBool() bool {
87+
switch k {
88+
case KeyNoPager, KeyQuiet, KeyNoInput:
89+
return true
90+
default:
91+
return false
92+
}
93+
}
94+
95+
// ValidValues returns valid values for enum keys, or nil if any value is valid
96+
func (k ConfigKey) ValidValues() []string {
97+
switch k {
98+
case KeyOutputFormat:
99+
return []string{"json", "yaml", "text"}
100+
case KeyNoPager, KeyQuiet, KeyNoInput:
101+
return []string{"true", "false"}
102+
default:
103+
return nil
104+
}
105+
}
106+
107+
// parseBoolOrDefault parses a boolean string, returning the default for empty strings
108+
func parseBoolOrDefault(value string, defaultVal bool) (bool, error) {
109+
if value == "" {
110+
return defaultVal, nil
111+
}
112+
return strconv.ParseBool(value)
113+
}
114+
115+
func SetConfigValue(conf *config.Config, key ConfigKey, value string, local bool) error {
116+
switch key {
117+
case KeySelectedOrg:
118+
return conf.SelectOrganization(value, local)
119+
case KeyOutputFormat:
120+
return conf.SetOutputFormat(value, local)
121+
case KeyNoPager:
122+
v, err := parseBoolOrDefault(value, false)
123+
if err != nil {
124+
return fmt.Errorf("invalid boolean value %q: %w", value, err)
125+
}
126+
return conf.SetNoPager(v, local)
127+
case KeyQuiet:
128+
v, err := parseBoolOrDefault(value, false)
129+
if err != nil {
130+
return fmt.Errorf("invalid boolean value %q: %w", value, err)
131+
}
132+
return conf.SetQuiet(v, local)
133+
case KeyNoInput:
134+
v, err := parseBoolOrDefault(value, false)
135+
if err != nil {
136+
return fmt.Errorf("invalid boolean value %q: %w", value, err)
137+
}
138+
return conf.SetNoInput(v)
139+
case KeyPager:
140+
return conf.SetPager(value)
141+
}
142+
143+
return nil
144+
}

0 commit comments

Comments
 (0)