Prepare for 1.0 release
- Fix Go version to 1.24.4 in go.mod - Add version management with version.go - Add --version flag to CLI tool - Remove deprecated LoadFromFile and LoadFromReader methods - Update tests to use new API - Create TODO.md for future improvements - Update README with Go version requirement Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
47801ce852
commit
82f09a16ec
@ -751,7 +751,7 @@ The `FILE` resolver can read any file accessible to the process:
|
||||
|
||||
## Requirements
|
||||
|
||||
- Go 1.18 or later
|
||||
- Go 1.24.4 or later
|
||||
- Valid YAML syntax in configuration files
|
||||
- Appropriate credentials for cloud providers (AWS, GCP, Azure)
|
||||
- Network access for remote resolvers (Vault, Consul, etcd)
|
||||
|
74
TODO.md
Normal file
74
TODO.md
Normal file
@ -0,0 +1,74 @@
|
||||
# TODO for smartconfig
|
||||
|
||||
This file tracks improvements and features planned for future releases.
|
||||
|
||||
## Testing Improvements
|
||||
|
||||
### Concurrency Tests
|
||||
- Add comprehensive concurrent access tests for Config struct
|
||||
- Test race conditions with multiple goroutines accessing/modifying config
|
||||
- Benchmark concurrent access patterns
|
||||
|
||||
### Security Tests
|
||||
- Add tests for malicious input handling in EXEC resolver
|
||||
- Test various command injection attempts
|
||||
- Add tests for FILE resolver with symlinks and directory traversal attempts
|
||||
|
||||
### Performance Tests
|
||||
- Add comprehensive benchmarks for all resolver types
|
||||
- Benchmark large configuration files (1MB+, 10MB+, 100MB+)
|
||||
- Benchmark nested interpolation performance
|
||||
- Compare performance with other config libraries
|
||||
|
||||
### Integration Tests
|
||||
- Add integration tests with mocked cloud services
|
||||
- Test cloud resolver authentication failures and retry scenarios
|
||||
- Test timeout behavior for all external resolvers
|
||||
|
||||
## Documentation
|
||||
|
||||
### Examples
|
||||
- Add godoc examples for all major functions
|
||||
- Create example applications demonstrating common use cases
|
||||
- Add examples for custom resolver implementation
|
||||
|
||||
### Guides
|
||||
- Write troubleshooting guide for common issues
|
||||
- Create migration guide from popular config libraries (Viper, koanf)
|
||||
- Write security best practices guide
|
||||
- Add performance tuning guide
|
||||
|
||||
## Future Features
|
||||
|
||||
### Caching Layer (if needed)
|
||||
- Consider adding optional caching for resolver results
|
||||
- Implement TTL-based cache invalidation
|
||||
- Add cache statistics and monitoring
|
||||
|
||||
### Configuration Validation
|
||||
- Add optional schema validation support
|
||||
- Implement type-safe configuration structs
|
||||
- Add validation rules for common patterns
|
||||
|
||||
### Watch/Reload Support (if needed)
|
||||
- Add file watching for configuration changes
|
||||
- Implement hot reload capability
|
||||
- Add change notification callbacks
|
||||
|
||||
### Enhanced Error Handling
|
||||
- Create typed errors for better error handling
|
||||
- Add error context with resolution path
|
||||
- Implement error aggregation for multiple failures
|
||||
|
||||
## Build and Release
|
||||
|
||||
### Release Automation
|
||||
- Set up goreleaser configuration
|
||||
- Automate changelog generation
|
||||
- Create release binaries for multiple platforms
|
||||
- Set up GitHub Actions for CI/CD
|
||||
|
||||
### Distribution
|
||||
- Publish to common package managers (Homebrew, apt, etc.)
|
||||
- Create Docker images with the CLI tool
|
||||
- Set up automated security scanning
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@ -12,9 +13,19 @@ import (
|
||||
|
||||
func main() {
|
||||
var jsonOutput bool
|
||||
var showVersion bool
|
||||
flag.BoolVar(&jsonOutput, "json", false, "Output as formatted JSON instead of YAML")
|
||||
flag.BoolVar(&showVersion, "version", false, "Show version information")
|
||||
flag.Parse()
|
||||
|
||||
if showVersion {
|
||||
fmt.Printf("smartconfig %s\n", smartconfig.Version)
|
||||
if smartconfig.GitCommit != "unknown" {
|
||||
fmt.Printf("commit: %s\n", smartconfig.GitCommit)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Read from stdin
|
||||
config, err := smartconfig.NewFromReader(os.Stdin)
|
||||
if err != nil {
|
||||
|
@ -122,48 +122,6 @@ func (c *Config) RegisterResolver(name string, resolver Resolver) {
|
||||
c.resolvers[name] = resolver
|
||||
}
|
||||
|
||||
// LoadFromFile loads configuration from a YAML file at the specified path.
|
||||
// DEPRECATED: Use NewFromConfigPath instead for cleaner API.
|
||||
func (c *Config) LoadFromFile(path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open config file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
return c.LoadFromReader(file)
|
||||
}
|
||||
|
||||
// LoadFromReader loads configuration from an io.Reader containing YAML data.
|
||||
// DEPRECATED: Use NewFromReader instead for cleaner API.
|
||||
func (c *Config) LoadFromReader(reader io.Reader) error {
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read config: %w", err)
|
||||
}
|
||||
|
||||
// Parse YAML first
|
||||
if err := yaml.Unmarshal(data, &c.data); err != nil {
|
||||
return fmt.Errorf("failed to parse YAML: %w", err)
|
||||
}
|
||||
|
||||
// Walk and interpolate the parsed structure
|
||||
interpolated, err := c.walkAndInterpolate(c.data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to interpolate config: %w", err)
|
||||
}
|
||||
c.data = interpolated.(map[string]interface{})
|
||||
|
||||
// Handle environment variable injection
|
||||
if err := c.injectEnvironment(); err != nil {
|
||||
return fmt.Errorf("failed to inject environment variables: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) injectEnvironment() error {
|
||||
envData, ok := c.data["env"]
|
||||
if !ok {
|
||||
|
@ -76,9 +76,8 @@ func TestInterpolateBasic(t *testing.T) {
|
||||
_ = os.Setenv("BASIC", "value")
|
||||
defer func() { _ = os.Unsetenv("BASIC") }()
|
||||
|
||||
config := New()
|
||||
content := "test: ${ENV:BASIC}"
|
||||
err := config.LoadFromReader(strings.NewReader(content))
|
||||
config, err := NewFromReader(strings.NewReader(content))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
@ -198,9 +197,8 @@ func TestSimpleNestedInterpolation(t *testing.T) {
|
||||
_ = os.Unsetenv("FOO_BAR")
|
||||
}()
|
||||
|
||||
config := New()
|
||||
content := "test: ${ENV:FOO_${ENV:SUFFIX}}"
|
||||
err := config.LoadFromReader(strings.NewReader(content))
|
||||
config, err := NewFromReader(strings.NewReader(content))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
@ -227,8 +225,7 @@ func TestConfigInterpolation(t *testing.T) {
|
||||
_ = os.Unsetenv("NESTED_VALUE")
|
||||
}()
|
||||
|
||||
config := New()
|
||||
err := config.LoadFromFile("./test/config.yaml")
|
||||
config, err := NewFromConfigPath("./test/config.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
@ -286,8 +283,6 @@ func TestConfigInterpolation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRecursionLimit(t *testing.T) {
|
||||
config := New()
|
||||
|
||||
// Create a deeply nested interpolation that hits the recursion limit
|
||||
// This has 4 levels, but our max is 3
|
||||
content := "value: ${ENV:LEVEL1_${ENV:LEVEL2_${ENV:LEVEL3_${ENV:LEVEL4}}}}"
|
||||
@ -311,7 +306,7 @@ func TestRecursionLimit(t *testing.T) {
|
||||
_ = os.Unsetenv("LEVEL1_depth2value")
|
||||
}()
|
||||
|
||||
err := config.LoadFromReader(strings.NewReader(content))
|
||||
config, err := NewFromReader(strings.NewReader(content))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
@ -346,11 +341,10 @@ func TestInvalidResolverFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnknownResolver(t *testing.T) {
|
||||
config := New()
|
||||
content := "value: ${UNKNOWN:test}"
|
||||
|
||||
// Unknown resolver should cause an error
|
||||
err := config.LoadFromReader(strings.NewReader(content))
|
||||
_, err := NewFromReader(strings.NewReader(content))
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for unknown resolver, but got none")
|
||||
}
|
||||
|
7
version.go
Normal file
7
version.go
Normal file
@ -0,0 +1,7 @@
|
||||
package smartconfig
|
||||
|
||||
// Version is the current version of smartconfig
|
||||
const Version = "1.0.0"
|
||||
|
||||
// GitCommit is the git commit hash (set at build time with -ldflags)
|
||||
var GitCommit = "unknown"
|
Loading…
Reference in New Issue
Block a user