Skip to content

Commit a3f2e41

Browse files
wass3rwass3rw3rk
andauthored
Refine color handling and add themes view (#624)
Only persist user-specified color theme in config and skip saving default terminal256 format. Auto-detect terminal background to pick light/dark themes, respect NO_COLOR, CLICOLOR_FORCE, and CLICOLOR, and allow overriding via --color flags. Add 'vela view themes' command to list available chroma themes and include tests for color behavior. Co-authored-by: David May <[email protected]>
1 parent bf0f955 commit a3f2e41

File tree

11 files changed

+1208
-23
lines changed

11 files changed

+1208
-23
lines changed

action/config/generate.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,22 @@ func genBytes(c *Config) ([]byte, error) {
7575
URL: c.GitHub.URL,
7676
},
7777
},
78-
Org: c.Org,
79-
Repo: c.Repo,
80-
Output: c.Output,
81-
Color: &c.Color.Enabled,
82-
ColorFormat: c.Color.Format,
83-
ColorTheme: c.Color.Theme,
84-
NoGit: c.NoGit,
78+
Org: c.Org,
79+
Repo: c.Repo,
80+
Output: c.Output,
81+
Color: &c.Color.Enabled,
82+
NoGit: c.NoGit,
83+
}
84+
85+
// only save if theme was user specified; this prevents saving default "monokai"
86+
// during login, which would bypass auto-detection for light terminal backgrounds.
87+
if c.Color.UserSpecified {
88+
config.ColorTheme = c.Color.Theme
89+
}
90+
91+
// only save if not default
92+
if c.Color.Format != "" && c.Color.Format != "terminal256" {
93+
config.ColorFormat = c.Color.Format
8594
}
8695

8796
out, err := yaml.Marshal(config)

action/config/generate_test.go

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ import (
66
"testing"
77

88
"github.com/spf13/afero"
9+
yaml "go.yaml.in/yaml/v3"
10+
11+
"github.com/go-vela/cli/internal/output"
12+
)
13+
14+
// Test constants for color configuration.
15+
const (
16+
testThemeMonokai = "monokai"
17+
testThemeMonokaiLight = "monokailight"
18+
testThemeDracula = "dracula"
19+
testThemeSolarizedDark = "solarized-dark"
20+
testFormatTerminal256 = "terminal256"
21+
testFormatTerminal16 = "terminal16"
22+
testFormatTerminal16m = "terminal16m"
923
)
1024

1125
func TestConfig_Config_Generate(t *testing.T) {
@@ -44,3 +58,306 @@ func TestConfig_Config_Generate(t *testing.T) {
4458
}
4559
}
4660
}
61+
62+
func TestConfig_Config_Generate_ColorTheme(t *testing.T) {
63+
// setup tests
64+
tests := []struct {
65+
name string
66+
config *Config
67+
expectTheme bool
68+
expectThemeValue string
69+
expectFormat bool
70+
expectFormatValue string
71+
}{
72+
{
73+
name: "default theme not user specified - should not save theme",
74+
config: &Config{
75+
Action: "generate",
76+
File: ".vela.yml",
77+
GitHub: &GitHub{},
78+
Color: output.ColorOptions{
79+
Enabled: true,
80+
Format: testFormatTerminal256,
81+
Theme: testThemeMonokai,
82+
ThemeLight: testThemeMonokaiLight,
83+
UserSpecified: false,
84+
},
85+
},
86+
expectTheme: false,
87+
expectFormat: false,
88+
},
89+
{
90+
name: "custom theme user specified - should save theme",
91+
config: &Config{
92+
Action: "generate",
93+
File: ".vela.yml",
94+
GitHub: &GitHub{},
95+
Color: output.ColorOptions{
96+
Enabled: true,
97+
Format: testFormatTerminal256,
98+
Theme: testThemeDracula,
99+
ThemeLight: testThemeMonokaiLight,
100+
UserSpecified: true,
101+
},
102+
},
103+
expectTheme: true,
104+
expectThemeValue: testThemeDracula,
105+
expectFormat: false,
106+
},
107+
{
108+
name: "default theme user specified - should save theme",
109+
config: &Config{
110+
Action: "generate",
111+
File: ".vela.yml",
112+
GitHub: &GitHub{},
113+
Color: output.ColorOptions{
114+
Enabled: true,
115+
Format: testFormatTerminal256,
116+
Theme: testThemeMonokai,
117+
ThemeLight: testThemeMonokaiLight,
118+
UserSpecified: true,
119+
},
120+
},
121+
expectTheme: true,
122+
expectThemeValue: testThemeMonokai,
123+
expectFormat: false,
124+
},
125+
{
126+
name: "custom format non-default - should save format",
127+
config: &Config{
128+
Action: "generate",
129+
File: ".vela.yml",
130+
GitHub: &GitHub{},
131+
Color: output.ColorOptions{
132+
Enabled: true,
133+
Format: testFormatTerminal16,
134+
Theme: testThemeMonokai,
135+
ThemeLight: testThemeMonokaiLight,
136+
UserSpecified: false,
137+
},
138+
},
139+
expectTheme: false,
140+
expectFormat: true,
141+
expectFormatValue: testFormatTerminal16,
142+
},
143+
{
144+
name: "custom format and theme both specified - should save both",
145+
config: &Config{
146+
Action: "generate",
147+
File: ".vela.yml",
148+
GitHub: &GitHub{},
149+
Color: output.ColorOptions{
150+
Enabled: true,
151+
Format: testFormatTerminal16m,
152+
Theme: testThemeSolarizedDark,
153+
ThemeLight: testThemeMonokaiLight,
154+
UserSpecified: true,
155+
},
156+
},
157+
expectTheme: true,
158+
expectThemeValue: testThemeSolarizedDark,
159+
expectFormat: true,
160+
expectFormatValue: testFormatTerminal16m,
161+
},
162+
{
163+
name: "empty format - should not save format",
164+
config: &Config{
165+
Action: "generate",
166+
File: ".vela.yml",
167+
GitHub: &GitHub{},
168+
Color: output.ColorOptions{
169+
Enabled: true,
170+
Format: "",
171+
Theme: testThemeMonokai,
172+
ThemeLight: testThemeMonokaiLight,
173+
UserSpecified: false,
174+
},
175+
},
176+
expectTheme: false,
177+
expectFormat: false,
178+
},
179+
{
180+
name: "default format terminal256 - should not save format",
181+
config: &Config{
182+
Action: "generate",
183+
File: ".vela.yml",
184+
GitHub: &GitHub{},
185+
Color: output.ColorOptions{
186+
Enabled: true,
187+
Format: testFormatTerminal256,
188+
Theme: testThemeMonokai,
189+
ThemeLight: testThemeMonokaiLight,
190+
UserSpecified: false,
191+
},
192+
},
193+
expectTheme: false,
194+
expectFormat: false,
195+
},
196+
}
197+
198+
// run tests
199+
for _, test := range tests {
200+
t.Run(test.name, func(t *testing.T) {
201+
// setup filesystem
202+
appFS = afero.NewMemMapFs()
203+
204+
err := test.config.Generate()
205+
if err != nil {
206+
t.Errorf("Generate returned err: %v", err)
207+
return
208+
}
209+
210+
// read the generated file
211+
a := &afero.Afero{Fs: appFS}
212+
213+
content, err := a.ReadFile(test.config.File)
214+
if err != nil {
215+
t.Errorf("Failed to read generated file: %v", err)
216+
return
217+
}
218+
219+
// parse the YAML
220+
var configFile ConfigFile
221+
222+
err = yaml.Unmarshal(content, &configFile)
223+
if err != nil {
224+
t.Errorf("Failed to unmarshal YAML: %v", err)
225+
return
226+
}
227+
228+
// check theme
229+
if test.expectTheme {
230+
if configFile.ColorTheme == "" {
231+
t.Errorf("Expected color_theme to be saved, but it was not")
232+
}
233+
234+
if configFile.ColorTheme != test.expectThemeValue {
235+
t.Errorf("ColorTheme = %v, want %v", configFile.ColorTheme, test.expectThemeValue)
236+
}
237+
} else {
238+
if configFile.ColorTheme != "" {
239+
t.Errorf("Expected color_theme to not be saved, but got: %v", configFile.ColorTheme)
240+
}
241+
}
242+
243+
// check format
244+
if test.expectFormat {
245+
if configFile.ColorFormat == "" {
246+
t.Errorf("Expected color_format to be saved, but it was not")
247+
}
248+
249+
if configFile.ColorFormat != test.expectFormatValue {
250+
t.Errorf("ColorFormat = %v, want %v", configFile.ColorFormat, test.expectFormatValue)
251+
}
252+
} else {
253+
if configFile.ColorFormat != "" {
254+
t.Errorf("Expected color_format to not be saved, but got: %v", configFile.ColorFormat)
255+
}
256+
}
257+
})
258+
}
259+
}
260+
261+
func TestConfig_genBytes_ColorTheme(t *testing.T) {
262+
// setup tests
263+
tests := []struct {
264+
name string
265+
config *Config
266+
wantTheme string
267+
wantFormat string
268+
wantNoTheme bool
269+
wantNoFormat bool
270+
}{
271+
{
272+
name: "user specified theme",
273+
config: &Config{
274+
GitHub: &GitHub{},
275+
Color: output.ColorOptions{
276+
Enabled: true,
277+
Theme: testThemeDracula,
278+
Format: testFormatTerminal256,
279+
UserSpecified: true,
280+
},
281+
},
282+
wantTheme: testThemeDracula,
283+
wantNoFormat: true, // default format shouldn't be saved
284+
},
285+
{
286+
name: "default theme not user specified",
287+
config: &Config{
288+
GitHub: &GitHub{},
289+
Color: output.ColorOptions{
290+
Enabled: true,
291+
Theme: testThemeMonokai,
292+
Format: testFormatTerminal256,
293+
UserSpecified: false,
294+
},
295+
},
296+
wantNoTheme: true,
297+
wantNoFormat: true,
298+
},
299+
{
300+
name: "custom format",
301+
config: &Config{
302+
GitHub: &GitHub{},
303+
Color: output.ColorOptions{
304+
Enabled: true,
305+
Theme: testThemeMonokai,
306+
Format: testFormatTerminal16,
307+
UserSpecified: false,
308+
},
309+
},
310+
wantFormat: testFormatTerminal16,
311+
wantNoTheme: true,
312+
},
313+
{
314+
name: "both theme and format custom",
315+
config: &Config{
316+
GitHub: &GitHub{},
317+
Color: output.ColorOptions{
318+
Enabled: true,
319+
Theme: testThemeSolarizedDark,
320+
Format: testFormatTerminal16m,
321+
UserSpecified: true,
322+
},
323+
},
324+
wantTheme: testThemeSolarizedDark,
325+
wantFormat: testFormatTerminal16m,
326+
},
327+
}
328+
329+
// run tests
330+
for _, test := range tests {
331+
t.Run(test.name, func(t *testing.T) {
332+
bytes, err := genBytes(test.config)
333+
if err != nil {
334+
t.Errorf("genBytes returned err: %v", err)
335+
return
336+
}
337+
338+
var configFile ConfigFile
339+
340+
err = yaml.Unmarshal(bytes, &configFile)
341+
if err != nil {
342+
t.Errorf("Failed to unmarshal YAML: %v", err)
343+
return
344+
}
345+
346+
if test.wantNoTheme {
347+
if configFile.ColorTheme != "" {
348+
t.Errorf("Expected no theme, but got: %v", configFile.ColorTheme)
349+
}
350+
} else if configFile.ColorTheme != test.wantTheme {
351+
t.Errorf("ColorTheme = %v, want %v", configFile.ColorTheme, test.wantTheme)
352+
}
353+
354+
if test.wantNoFormat {
355+
if configFile.ColorFormat != "" {
356+
t.Errorf("Expected no format, but got: %v", configFile.ColorFormat)
357+
}
358+
} else if configFile.ColorFormat != test.wantFormat {
359+
t.Errorf("ColorFormat = %v, want %v", configFile.ColorFormat, test.wantFormat)
360+
}
361+
})
362+
}
363+
}

cmd/vela-cli/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func main() {
137137
&cli.StringFlag{
138138
Sources: cli.EnvVars("VELA_COLOR_THEME"),
139139
Name: internal.FlagColorTheme,
140-
Usage: "configures the output color theme (default: monokai)",
140+
Usage: "configures the output color theme (default: monokai or monokailight) - use 'vela view themes' to see available themes",
141141
},
142142
}
143143

cmd/vela-cli/view.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/go-vela/cli/command/service"
1919
"github.com/go-vela/cli/command/settings"
2020
"github.com/go-vela/cli/command/step"
21+
"github.com/go-vela/cli/command/themes"
2122
"github.com/go-vela/cli/command/user"
2223
"github.com/go-vela/cli/command/worker"
2324
)
@@ -96,6 +97,11 @@ var viewCmds = &cli.Command{
9697
// https://pkg.go.dev/github.com/go-vela/cli/command/step?tab=doc#CommandView
9798
step.CommandView,
9899

100+
// add the sub command for viewing available themes
101+
//
102+
// https://pkg.go.dev/github.com/go-vela/cli/command/themes?tab=doc#CommandView
103+
themes.CommandView,
104+
99105
// add the sub command for viewing a user
100106
//
101107
// https://pkg.go.dev/github.com/go-vela/cli/command/user?tab=doc#CommandView

command/themes/doc.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
// Package themes provides the defined CLI theme commands for Vela.
4+
//
5+
// Usage:
6+
//
7+
// import "github.com/go-vela/cli/command/themes"
8+
package themes

0 commit comments

Comments
 (0)