Skip to content

iamolegga/goenvsubst

Repository files navigation

goenvsubst

Go Reference Go Report Card GitHub Actions Workflow Status Codacy Badge Codacy Badge License: MIT

A Go package for recursively replacing environment variable references in Go data structures with their actual values from the environment.

Features

  • Comprehensive Type Support: Works with structs, slices, maps, arrays, and pointers
  • Nested Structures: Handles deeply nested and complex data structures
  • Safe Pointer Handling: Safely processes nil pointers without panics
  • In-Place Modification: Modifies data structures in-place for efficiency
  • Environment Variable Format: Uses $VAR_NAME format for variable references
  • Missing Variable Handling: Replaces undefined or empty variables with empty strings
  • Zero Dependencies: Pure Go implementation with no external dependencies

Installation

go get github.com/iamolegga/goenvsubst

Quick Start

package main

import (
    "fmt"
    "os"
    "github.com/iamolegga/goenvsubst"
)

func main() {
    // Set environment variable
    os.Setenv("DATABASE_URL", "postgres://localhost:5432/mydb")
    
    config := &struct {
        DatabaseURL string
        Debug       bool
    }{
        DatabaseURL: "$DATABASE_URL",
        Debug:       true,
    }
    
    err := goenvsubst.Do(config)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Database URL: %s\n", config.DatabaseURL)
    // Output: Database URL: postgres://localhost:5432/mydb
}

Usage Examples

Struct Fields

type Config struct {
    Host     string
    Port     string
    Username string
    Password string
}

config := &Config{
    Host:     "$DB_HOST",
    Port:     "$DB_PORT", 
    Username: "$DB_USER",
    Password: "$DB_PASS",
}

goenvsubst.Do(config)

Slices and Arrays

// Slice
urls := &[]string{"$API_URL", "$BACKUP_URL", "https://static.example.com"}
goenvsubst.Do(urls)

// Array
servers := &[3]string{"$SERVER1", "$SERVER2", "$SERVER3"}
goenvsubst.Do(servers)

Maps

config := &map[string]string{
    "database_url": "$DATABASE_URL",
    "redis_url":    "$REDIS_URL",
    "api_key":      "$API_KEY",
}
goenvsubst.Do(config)
// Note: Map keys are never modified, only values

Complex Nested Structures

type DatabaseConfig struct {
    URL      string
    Username string
    Password string
}

type AppConfig struct {
    Database DatabaseConfig
    Services []string
    Env      map[string]string
}

config := &AppConfig{
    Database: DatabaseConfig{
        URL:      "$DATABASE_URL",
        Username: "$DB_USER",
        Password: "$DB_PASS",
    },
    Services: []string{"$SERVICE1", "$SERVICE2"},
    Env: map[string]string{
        "LOG_LEVEL": "$LOG_LEVEL",
        "DEBUG":     "$DEBUG_MODE",
    },
}

goenvsubst.Do(config)

Pointers

// Safe with nil pointers
var config *struct{ Value string }
goenvsubst.Do(&config) // No error, no operation

// Works with actual pointers
value := "$MY_VALUE"
ptr := &value
goenvsubst.Do(&ptr)

Supported Data Types

Type Support Notes
string Environment variables are substituted
struct All string fields are processed recursively
slice All elements are processed recursively
array All elements are processed recursively
map Only values are processed, keys remain unchanged
pointer Safely handles nil pointers
int, bool, etc. Non-string types are ignored (no substitution)
interface{} Not currently supported

Environment Variable Format

Environment variables should be referenced using the $VAR_NAME format:

// Supported formats
"$DATABASE_URL"           // ✅ Full string replacement
"$MISSING_VAR"            // ✅ Undefined vars become empty strings

// Not supported formats
"${DATABASE_URL}"         // ❌ Braces not supported
"prefix-$API_KEY-suffix"  // ❌ Partial substitution not supported

Error Handling

The Do function returns an error for future extensibility, but currently is designed to be robust:

err := goenvsubst.Do(config)
if err != nil {
    log.Printf("Failed to substitute environment variables: %v", err)
    return
}

Important Notes

  • In-Place Modification: The function modifies the input data structure directly
  • Map Keys: Only map values are processed, keys are never modified
  • Missing Variables: Undefined or empty environment variables are replaced with empty strings
  • Thread Safety: Safe for concurrent use (doesn't modify global state)
  • Nil Pointers: Handled safely without causing panics
  • Type Safety: Only string values are processed for substitution

Testing

Run the tests:

go test -v

Run example tests:

go test -v -run Example

Documentation

For detailed documentation and more examples, visit: pkg.go.dev Documentation

License

MIT License - see LICENSE file for details.