Add flexible configuration with custom file path support
\n- Add DLC_CONFIG_FILE environment variable for custom config paths\n- Update config.go to support both default and custom config file locations\n- Update README.md and AGENTS.md with configuration documentation\n- Remove /Users/gabrielradureau/.dancelessonscoach/ directory approach\n- Add os import to config.go\n- Test all configuration scenarios
This commit is contained in:
16
AGENTS.md
16
AGENTS.md
@@ -141,7 +141,15 @@ go run cmd/server/main.go &
|
|||||||
|
|
||||||
**Configuration File Support:**
|
**Configuration File Support:**
|
||||||
|
|
||||||
A `config.example.yaml` file is provided as a template:
|
A `config.example.yaml` file is provided as a template. By default, the application looks for `config.yaml` in the current working directory.
|
||||||
|
|
||||||
|
To specify a custom config file path, set the `DLC_CONFIG_FILE` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DLC_CONFIG_FILE="/path/to/config.yaml" go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `config.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
server:
|
server:
|
||||||
@@ -153,10 +161,12 @@ shutdown:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Configuration Loading:**
|
**Configuration Loading:**
|
||||||
- Environment variables take precedence over defaults
|
- **File-based configuration** takes highest precedence
|
||||||
|
- **Environment variables** override defaults but are overridden by config file
|
||||||
|
- **Default values** are used when no other configuration is provided
|
||||||
- All configuration is validated on startup
|
- All configuration is validated on startup
|
||||||
- Invalid configurations cause server startup failure
|
- Invalid configurations cause server startup failure
|
||||||
- Configuration values are logged at startup
|
- Configuration values and source are logged at startup
|
||||||
|
|
||||||
**Verification:**
|
**Verification:**
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
104
README.md
104
README.md
@@ -1,11 +1,16 @@
|
|||||||
# DanceLessonsCoach
|
# DanceLessonsCoach
|
||||||
|
|
||||||
A simple Go project demonstrating idiomatic package structure and CLI implementation.
|
A Go project demonstrating idiomatic package structure, CLI implementation, and JSON API with Chi router.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Greet function with default behavior
|
- Greet function with default behavior
|
||||||
- Command-line interface
|
- Command-line interface
|
||||||
|
- JSON API with versioned endpoints
|
||||||
|
- Chi router integration
|
||||||
|
- Zerolog for high-performance logging
|
||||||
|
- Viper for configuration management
|
||||||
|
- Graceful shutdown with context
|
||||||
- Unit tests
|
- Unit tests
|
||||||
- Go 1.26.1 compatible
|
- Go 1.26.1 compatible
|
||||||
|
|
||||||
@@ -20,8 +25,70 @@ cd DanceLessonsCoach
|
|||||||
go run ./cmd/greet
|
go run ./cmd/greet
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Optional Configuration
|
||||||
|
|
||||||
|
The project supports configuration via YAML file, environment variables, or defaults.
|
||||||
|
Configuration priority: file > environment variables > defaults
|
||||||
|
|
||||||
|
### Configuration File
|
||||||
|
|
||||||
|
By default, the application looks for `config.yaml` in the current working directory.
|
||||||
|
|
||||||
|
Create a `config.yaml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
server:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
shutdown:
|
||||||
|
timeout: 30s
|
||||||
|
```
|
||||||
|
|
||||||
|
Then start the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Config File Path
|
||||||
|
|
||||||
|
To specify a custom config file path, use the `DLC_CONFIG_FILE` environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use a specific config file
|
||||||
|
export DLC_CONFIG_FILE="/path/to/your/config.yaml"
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
# Or in one command
|
||||||
|
DLC_CONFIG_FILE="/path/to/your/config.yaml" go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
You can also configure via environment variables with `DLC_` prefix:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set configuration via environment variables
|
||||||
|
export DLC_SERVER_HOST="0.0.0.0"
|
||||||
|
export DLC_SERVER_PORT=8080
|
||||||
|
export DLC_SHUTDOWN_TIMEOUT=30s
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Priority
|
||||||
|
|
||||||
|
1. **File-based configuration** (highest priority)
|
||||||
|
2. **Environment variables** (override defaults)
|
||||||
|
3. **Default values** (lowest priority)
|
||||||
|
|
||||||
|
This means if you have both a config file and environment variables, the file takes precedence.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Default greeting
|
# Default greeting
|
||||||
go run ./cmd/greet
|
go run ./cmd/greet
|
||||||
@@ -32,6 +99,23 @@ go run ./cmd/greet John
|
|||||||
# Output: Hello John!
|
# Output: Hello John!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Web Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start the server
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
# Test API endpoints
|
||||||
|
curl http://localhost:8080/api/health
|
||||||
|
# Output: {"status":"healthy"}
|
||||||
|
|
||||||
|
curl http://localhost:8080/api/v1/greet
|
||||||
|
# Output: {"message":"Hello world!"}
|
||||||
|
|
||||||
|
curl http://localhost:8080/api/v1/greet/John
|
||||||
|
# Output: {"message":"Hello John!"}
|
||||||
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -47,12 +131,20 @@ go test ./pkg/greet/
|
|||||||
```
|
```
|
||||||
DanceLessonsCoach/
|
DanceLessonsCoach/
|
||||||
├── cmd/
|
├── cmd/
|
||||||
│ └── greet/
|
│ ├── greet/ # CLI entry point
|
||||||
│ └── main.go # CLI entry point
|
│ │ └── main.go
|
||||||
|
│ └── server/ # Web server entry point
|
||||||
|
│ └── main.go
|
||||||
├── pkg/
|
├── pkg/
|
||||||
│ └── greet/
|
│ ├── config/ # Configuration management
|
||||||
│ ├── greet.go # Core library
|
│ │ └── config.go
|
||||||
│ └── greet_test.go # Unit tests
|
│ ├── greet/ # Core greet functionality
|
||||||
|
│ │ ├── api_v1.go # API v1 handlers
|
||||||
|
│ │ ├── greet.go # Core service
|
||||||
|
│ │ └── greet_test.go # Unit tests
|
||||||
|
│ └── server/ # Server implementation
|
||||||
|
│ └── server.go
|
||||||
|
├── config.example.yaml # Configuration template
|
||||||
├── go.mod # Go module definition
|
├── go.mod # Go module definition
|
||||||
└── README.md # Project documentation
|
└── README.md # Project documentation
|
||||||
```
|
```
|
||||||
|
|||||||
5
config.yaml
Normal file
5
config.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
server:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
shutdown:
|
||||||
|
timeout: 5s
|
||||||
@@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -19,7 +20,9 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig loads configuration from environment variables and defaults
|
// LoadConfig loads configuration from file, environment variables, and defaults
|
||||||
|
// Configuration priority: file > environment variables > defaults
|
||||||
|
// To specify a custom config file path, set DLC_CONFIG_FILE environment variable
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
|
|
||||||
@@ -28,6 +31,28 @@ func LoadConfig() (*Config, error) {
|
|||||||
v.SetDefault("server.port", 8080)
|
v.SetDefault("server.port", 8080)
|
||||||
v.SetDefault("shutdown.timeout", 30*time.Second)
|
v.SetDefault("shutdown.timeout", 30*time.Second)
|
||||||
|
|
||||||
|
// Check for custom config file path via environment variable
|
||||||
|
if configFile := os.Getenv("DLC_CONFIG_FILE"); configFile != "" {
|
||||||
|
v.SetConfigFile(configFile)
|
||||||
|
log.Info().Str("config_file", configFile).Msg("Using custom config file path")
|
||||||
|
} else {
|
||||||
|
// Default: look for config.yaml in current directory
|
||||||
|
v.SetConfigName("config")
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
v.AddConfigPath(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read config file if it exists
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
|
// Config file was found but there was an error reading it
|
||||||
|
log.Warn().Err(err).Msg("Error reading config file, using defaults")
|
||||||
|
}
|
||||||
|
// Config file not found, continue with environment variables and defaults
|
||||||
|
} else {
|
||||||
|
log.Info().Str("config_file", v.ConfigFileUsed()).Msg("Config file loaded")
|
||||||
|
}
|
||||||
|
|
||||||
// Bind environment variables
|
// Bind environment variables
|
||||||
v.AutomaticEnv()
|
v.AutomaticEnv()
|
||||||
v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix
|
v.SetEnvPrefix("DLC") // DanceLessonsCoach prefix
|
||||||
|
|||||||
Reference in New Issue
Block a user