Skip to content

Commit a51feb6

Browse files
committed
add ENV Vars for Debug & Trace
1 parent aab0049 commit a51feb6

File tree

8 files changed

+172
-22
lines changed

8 files changed

+172
-22
lines changed

.vscode/launch.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
"request": "launch",
1111
"mode": "debug",
1212
"program": "main.go",
13+
"env": {
14+
"VIMBIN_THEME": "dark"
15+
,"VIMBIN_DEBUG": "true"
16+
},
1317
"args": [
14-
"--config"
15-
,"./.vimbin.yaml"
16-
,"--trace"
18+
"serve"
19+
1720
]
1821
}
1922
]

README.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
| Flag | Description |
1414
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
1515
| `-c`, `--config` `PATH` | Path to the configuration file. |
16-
| `--debug` | Activates debug output for detailed logging. |
16+
| `--debug` | Activates debug output for detailed logging. Can also be set with the environment variable `VIMBIN_DEBUG` |
1717
| `-t`, `--token` `TOKEN` | Token to use for authentication. If not set, a random token will be generated. Can also be set with the environment variable `VIMBIN_TOKEN` |
18-
| `--trace` | Enables trace mode. This will show the content in the logs! |
18+
| `--trace` | Enables trace mode. This will show the content in the logs! Can also be set with the environment variable `VIMBIN_TRACE` |
1919
| `-v`, `--version` | Print version and exit. |
2020

2121
### Serve
@@ -28,13 +28,13 @@ Start the server:
2828

2929
**Flags:**
3030

31-
| Flag | Description |
32-
| :-------------------------------------- | :----------------------------------------------------------------------------------------------- |
33-
| `-d`, `--directory` `DIRECTORY` | The path to the storage directory. Defaults to the current working directory. (default `$(pwd)`) |
34-
| `-a`, `--listen-address` `ADDRESS:PORT` | The address to listen on for HTTP requests. (default `:8080`) |
35-
| `-n`, `--name` string | The name of the file to save. (default ".vimbin") |
36-
| `--theme` THEME | The theme to use. Can be `auto`, `light` or `dark`. (default `auto`) |
37-
| `-h`, `--help` | help for serve |
31+
| Flag | Description |
32+
| :-------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- |
33+
| `-d`, `--directory` `DIRECTORY` | The path to the storage directory. (default `$(pwd)`) |
34+
| `-a`, `--listen-address` `ADDRESS:PORT` | The address to listen on for HTTP requests. (default `:8080`) |
35+
| `-n`, `--name` string | The name of the file to save. (default ".vimbin") |
36+
| `--theme` THEME | The theme to use. Can be `auto`, `light` or `dark`. (default `auto`). Can also be set with the environment variable `VIMBIN_THEME` |
37+
| `-h`, `--help` | help for serve |
3838

3939
### Push
4040

cmd/root.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"os"
2121
"time"
2222
"vimbin/internal/config"
23+
"vimbin/internal/utils"
2324

2425
"github.com/rs/zerolog"
2526
"github.com/rs/zerolog/log"
@@ -67,14 +68,23 @@ var rootCmd = &cobra.Command{
6768
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
6869
log.Logger = zerolog.New(output).With().Timestamp().Logger()
6970

70-
// Configure log levels based on debug and trace flags
71+
// Retrieve debug and trace flags from environment variables if not explicitly set
72+
if !debug || !trace {
73+
var err error
74+
if debug, trace, err = utils.GetDebugAndTrace(); err != nil {
75+
log.Fatal().Msgf("Failed to retrieve debug and trace flags. %s", err)
76+
}
77+
}
78+
79+
// Set the log level
7180
if debug {
7281
zerolog.SetGlobalLevel(zerolog.DebugLevel)
7382
log.Debug().Msgf("Verbose output enabled")
7483
} else if trace {
7584
zerolog.SetGlobalLevel(zerolog.TraceLevel)
7685
log.Debug().Msgf("Trace output enabled")
7786
}
87+
7888
if token := cmd.Flag("token").Value.String(); token != "" {
7989
config.App.Server.Api.Token.Set(token)
8090
}
@@ -116,10 +126,11 @@ func init() {
116126

117127
// initConfig reads the configuration from the specified file or environment variables.
118128
func initConfig() {
129+
viper.AutomaticEnv() // Read in environment variables that match
130+
119131
if cfgFile != "" {
120132
// Use the config file specified by the flag.
121133
viper.SetConfigFile(cfgFile)
122-
viper.AutomaticEnv() // Read in environment variables that match
123134

124135
if err := config.App.Read(viper.ConfigFileUsed()); err != nil {
125136
log.Fatal().Msgf("Error reading config file: %v", err)

cmd/serve.go

+4-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package cmd
1818
import (
1919
"fmt"
2020
"os"
21-
"strings"
2221
"text/template"
2322
"vimbin/internal/config"
2423
"vimbin/internal/handlers"
@@ -30,9 +29,6 @@ import (
3029
"github.com/spf13/cobra"
3130
)
3231

33-
// supportedThemes is a list of themes supported by the serve command.
34-
var supportedThemes = []string{"auto", "light", "dark"}
35-
3632
// serveCmd represents the serve command.
3733
var serveCmd = &cobra.Command{
3834
Use: "serve",
@@ -42,8 +38,8 @@ var serveCmd = &cobra.Command{
4238
manipulate text using familiar Vim motions for an enhanced editing experience.`,
4339
Run: func(cmd *cobra.Command, args []string) {
4440
// Check if the specified theme is supported
45-
if !utils.IsInList(config.App.Server.Web.Theme, supportedThemes) {
46-
fmt.Printf("Unsupported output format: %s. Supported formats are: %s\n", config.App.Server.Web.Theme, strings.Join(supportedThemes, ", "))
41+
if !utils.IsInList(config.App.Server.Web.Theme, config.SupportedThemes) {
42+
fmt.Printf("Unsupported output format: %s. Supported formats are: %s\n", config.App.Server.Web.Theme, config.SupportedThemes)
4743
_ = cmd.Help() // Makes the linter happy
4844
os.Exit(1)
4945
}
@@ -74,9 +70,9 @@ func init() {
7470
// Define command-line flags for the serve command
7571
serveCmd.PersistentFlags().StringVarP(&config.App.Server.Web.Address, "listen-address", "a", ":8080", "The address to listen on for HTTP requests.")
7672

77-
serveCmd.PersistentFlags().StringVarP(&config.App.Server.Web.Theme, "theme", "", "auto", fmt.Sprintf("The theme to use. Can be %s.", strings.Join(supportedThemes, "|")))
73+
serveCmd.PersistentFlags().StringVarP(&config.App.Server.Web.Theme, "theme", "", "auto", fmt.Sprintf("The theme to use. Can be %s.", config.SupportedThemes))
7874
serveCmd.RegisterFlagCompletionFunc("theme", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
79-
return supportedThemes, cobra.ShellCompDirectiveDefault
75+
return config.SupportedThemes, cobra.ShellCompDirectiveDefault
8076
})
8177

8278
serveCmd.PersistentFlags().StringVarP(&config.App.Storage.Directory, "directory", "d", "$(pwd)", "The path to the storage directory. Defaults to the current working directory.")

internal/config/parse.go

+10
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func (c *Config) Parse() (err error) {
5353
// Check if the API token was set as ENV variable
5454
if token := os.Getenv("VIMBIN_TOKEN"); token != "" {
5555
c.Server.Api.Token.Set(token)
56+
log.Debug().Msgf("Using API token from ENV variable: %s", token)
5657
}
5758

5859
// Check if the API token is valid
@@ -63,5 +64,14 @@ func (c *Config) Parse() (err error) {
6364
log.Debug().Msgf("Generated API token: %s", c.Server.Api.Token.Get())
6465
}
6566

67+
// Check if the theme was set as ENV variable
68+
if theme := os.Getenv("VIMBIN_THEME"); theme != "" {
69+
if !utils.IsInList(theme, SupportedThemes) {
70+
return fmt.Errorf("Unsupported theme: %s. Supported themes are: %s", theme, SupportedThemes)
71+
}
72+
c.Server.Web.Theme = theme
73+
log.Debug().Msgf("Using theme from ENV variable: %s", theme)
74+
}
75+
6676
return nil
6777
}

internal/config/utils.go

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"reflect"
7+
"strings"
78

89
"github.com/mitchellh/mapstructure"
910
"github.com/rs/zerolog/log"
@@ -29,6 +30,17 @@ return (--n >= 0) ? (unsigned char) *bufp++ : EOF;
2930
}
3031
`
3132

33+
// Themes is a list of themes supported by the serve command.
34+
type Themes []string
35+
36+
// SupportedThemes is a list of themes supported by the serve command.
37+
var SupportedThemes = Themes{"auto", "light", "dark"}
38+
39+
// String returns the list of themes as a string.
40+
func (t Themes) String() string {
41+
return strings.Join(t, ", ")
42+
}
43+
3244
// checkStorageFile checks if the storage file exists; if not, it creates it with default content.
3345
//
3446
// Parameters:

internal/utils/utils.go

+45
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,54 @@ import (
77
"fmt"
88
"net"
99
"net/http"
10+
"os"
1011
"strconv"
1112
)
1213

14+
// Environment variable names
15+
const (
16+
VIMBINDebugEnv = "VIMBIN_DEBUG"
17+
VIMBINTraceEnv = "VIMBIN_TRACE"
18+
)
19+
20+
// GetDebugAndTrace retrieves the debug and trace flags from environment variables.
21+
//
22+
// The function checks the "VIMBIN_DEBUG" and "VIMBIN_TRACE" environment variables,
23+
// parses their values, and returns the corresponding boolean flags.
24+
//
25+
// Returns:
26+
// - debug: bool
27+
// True if "VIMBIN_DEBUG" is set to "true", false otherwise.
28+
// - trace: bool
29+
// True if "VIMBIN_TRACE" is set to "true", false otherwise.
30+
// - err: error
31+
// An error if there was an issue parsing the environment variables or if
32+
// both "VIMBIN_DEBUG" and "VIMBIN_TRACE" are set simultaneously.
33+
func GetDebugAndTrace() (debug bool, trace bool, err error) {
34+
// Check and parse VIMBIN_DEBUG environment variable
35+
if debugEnv := os.Getenv(VIMBINDebugEnv); debugEnv != "" {
36+
debug, err = strconv.ParseBool(debugEnv)
37+
if err != nil {
38+
return false, false, fmt.Errorf("Unable to parse '%s'. %s", VIMBINDebugEnv, err)
39+
}
40+
}
41+
42+
// Check and parse VIMBIN_TRACE environment variable
43+
if traceEnv := os.Getenv(VIMBINTraceEnv); traceEnv != "" {
44+
trace, err = strconv.ParseBool(traceEnv)
45+
if err != nil {
46+
return false, false, fmt.Errorf("Unable to parse '%s'. %s", VIMBINTraceEnv, err)
47+
}
48+
}
49+
50+
// Check for mutual exclusivity of debug and trace
51+
if debug && trace {
52+
return false, false, fmt.Errorf("'%s' and '%s' are mutually exclusive", VIMBINDebugEnv, VIMBINTraceEnv)
53+
}
54+
55+
return debug, trace, nil
56+
}
57+
1358
// IsInList checks if a value is in a list.
1459
//
1560
// Parameters:

internal/utils/utils_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,82 @@ package utils
22

33
import (
44
"net/http"
5+
"os"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
89
)
910

11+
func TestGetDebugAndTrace(t *testing.T) {
12+
t.Run("Both debug and trace are set", func(t *testing.T) {
13+
os.Setenv(VIMBINDebugEnv, "true")
14+
os.Setenv(VIMBINTraceEnv, "true")
15+
defer os.Unsetenv(VIMBINDebugEnv)
16+
defer os.Unsetenv(VIMBINTraceEnv)
17+
18+
debug, trace, err := GetDebugAndTrace()
19+
20+
assert.False(t, debug)
21+
assert.False(t, trace)
22+
assert.Error(t, err)
23+
assert.Contains(t, err.Error(), "'VIMBIN_DEBUG' and 'VIMBIN_TRACE' are mutually exclusive")
24+
})
25+
26+
t.Run("Invalid debug value", func(t *testing.T) {
27+
os.Setenv(VIMBINDebugEnv, "invalid")
28+
defer os.Unsetenv(VIMBINDebugEnv)
29+
30+
debug, trace, err := GetDebugAndTrace()
31+
32+
assert.False(t, debug)
33+
assert.False(t, trace)
34+
assert.Error(t, err)
35+
assert.Contains(t, err.Error(), "Unable to parse 'VIMBIN_DEBUG'")
36+
})
37+
38+
t.Run("Invalid trace value", func(t *testing.T) {
39+
os.Setenv(VIMBINTraceEnv, "invalid")
40+
defer os.Unsetenv(VIMBINTraceEnv)
41+
42+
debug, trace, err := GetDebugAndTrace()
43+
44+
assert.False(t, debug)
45+
assert.False(t, trace)
46+
assert.Error(t, err)
47+
assert.Contains(t, err.Error(), "Unable to parse 'VIMBIN_TRACE'")
48+
})
49+
50+
t.Run("Debug is set", func(t *testing.T) {
51+
os.Setenv(VIMBINDebugEnv, "true")
52+
defer os.Unsetenv(VIMBINDebugEnv)
53+
54+
debug, trace, err := GetDebugAndTrace()
55+
56+
assert.True(t, debug)
57+
assert.False(t, trace)
58+
assert.NoError(t, err)
59+
})
60+
61+
t.Run("Trace is set", func(t *testing.T) {
62+
os.Setenv(VIMBINTraceEnv, "true")
63+
defer os.Unsetenv(VIMBINTraceEnv)
64+
65+
debug, trace, err := GetDebugAndTrace()
66+
67+
assert.False(t, debug)
68+
assert.True(t, trace)
69+
assert.NoError(t, err)
70+
})
71+
72+
t.Run("Neither debug nor trace is set", func(t *testing.T) {
73+
debug, trace, err := GetDebugAndTrace()
74+
75+
assert.False(t, debug)
76+
assert.False(t, trace)
77+
assert.NoError(t, err)
78+
})
79+
}
80+
1081
func TestIsInList(t *testing.T) {
1182
t.Run("Value is in the list", func(t *testing.T) {
1283
value := "apple"
@@ -53,6 +124,7 @@ func TestExtractHostAndPort(t *testing.T) {
53124
_, _, err := ExtractHostAndPort(address)
54125

55126
assert.Error(t, err)
127+
assert.Contains(t, err.Error(), "missing port in address")
56128
})
57129

58130
t.Run("Invalid port", func(t *testing.T) {
@@ -61,6 +133,7 @@ func TestExtractHostAndPort(t *testing.T) {
61133
_, _, err := ExtractHostAndPort(address)
62134

63135
assert.Error(t, err)
136+
assert.Contains(t, err.Error(), "parsing \"invalid\"")
64137
})
65138
}
66139

0 commit comments

Comments
 (0)