Skip to content

Commit 837433b

Browse files
committed
feat(database): integrate dotenv for environment variable management
- Added support for loading environment variables from .env files using the `joho/godotenv` package. - Implemented `loadEnvFiles` method to load environment variables in a prioritized manner, allowing for environment-specific configurations. - Updated `loadDatabaseConfig` to ensure .env files are loaded before creating the ConfigManager, enhancing configuration flexibility.
1 parent 05de831 commit 837433b

4 files changed

Lines changed: 60 additions & 0 deletions

File tree

cmd/forge/plugins/database.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"path/filepath"
88
"strings"
99

10+
"github.com/joho/godotenv"
1011
"github.com/uptrace/bun"
1112
"github.com/uptrace/bun/migrate"
1213

@@ -839,6 +840,11 @@ func (p *DatabasePlugin) getDatabaseConnection(ctx cli.CommandContext) (*bun.DB,
839840
// This provides automatic environment variable expansion, file merging, and proper
840841
// namespace support for both 'database' and 'extensions.database' keys.
841842
func (p *DatabasePlugin) loadDatabaseConfig(dbName, appName string) (database.DatabaseConfig, error) {
843+
// CRITICAL: Load .env files BEFORE creating ConfigManager
844+
// ConfigManager expands environment variables when reading config files,
845+
// so .env vars must be in the environment at that point
846+
p.loadEnvFiles()
847+
842848
// Create a temporary Forge app to access ConfigManager
843849
// This gives us all the benefits: file discovery, merging, env var expansion, etc.
844850
app := forge.NewApp(forge.AppConfig{
@@ -848,6 +854,7 @@ func (p *DatabasePlugin) loadDatabaseConfig(dbName, appName string) (database.Da
848854
// Enable config auto-discovery to find config.yaml and config.local.yaml
849855
EnableConfigAutoDiscovery: true,
850856
ConfigSearchPaths: []string{p.config.RootDir, filepath.Join(p.config.RootDir, "config")},
857+
Logger: forge.NewNoopLogger(),
851858
})
852859

853860
cm := app.Config()
@@ -923,6 +930,55 @@ func getDatabaseNames(databases []database.DatabaseConfig) []string {
923930
return names
924931
}
925932

933+
// loadEnvFiles loads environment variables from .env files.
934+
// Loads in order of priority (later files override earlier ones):
935+
// 1. .env (base configuration)
936+
// 2. .env.local (local overrides, gitignored)
937+
// 3. .env.{environment} (environment-specific)
938+
// 4. .env.{environment}.local (environment-specific local overrides)
939+
//
940+
// This follows the standard dotenv convention used by many frameworks.
941+
func (p *DatabasePlugin) loadEnvFiles() {
942+
if p.config == nil {
943+
return
944+
}
945+
946+
// Determine environment (default to development)
947+
env := os.Getenv("FORGE_ENV")
948+
if env == "" {
949+
env = os.Getenv("GO_ENV")
950+
}
951+
if env == "" {
952+
env = "development"
953+
}
954+
955+
// Files to load in priority order (earlier = lower priority)
956+
envFiles := []string{
957+
filepath.Join(p.config.RootDir, ".env"),
958+
filepath.Join(p.config.RootDir, ".env.local"),
959+
}
960+
961+
// Add environment-specific files
962+
if env != "" {
963+
envFiles = append(envFiles,
964+
filepath.Join(p.config.RootDir, fmt.Sprintf(".env.%s", env)),
965+
filepath.Join(p.config.RootDir, fmt.Sprintf(".env.%s.local", env)),
966+
)
967+
}
968+
969+
// Load each file that exists
970+
for _, envFile := range envFiles {
971+
if _, err := os.Stat(envFile); err == nil {
972+
// Load without overriding existing env vars (godotenv.Load would override)
973+
// We use Overload to ensure later files take precedence
974+
if err := godotenv.Overload(envFile); err != nil {
975+
// Silently continue - .env files are optional
976+
continue
977+
}
978+
}
979+
}
980+
}
981+
926982
// cliLoggerAdapter adapts CLI context to database.Logger interface.
927983
type cliLoggerAdapter struct {
928984
ctx cli.CommandContext

cmd/forge/plugins/database_go_migrations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func (p *DatabasePlugin) runWithGoMigrations(ctx cli.CommandContext, command str
6868
}
6969

7070
// Get database config
71+
// Note: .env files are loaded inside loadDatabaseConfig() before ConfigManager is created
7172
// Note: ConfigManager already expands environment variables
7273
dbConfig, err := p.loadDatabaseConfig(dbName, ctx.String("app"))
7374
if err != nil {

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ require (
122122
github.com/hashicorp/serf v0.10.1 // indirect
123123
github.com/itlightning/dateparse v0.2.0 // indirect
124124
github.com/jinzhu/inflection v1.0.0 // indirect
125+
github.com/joho/godotenv v1.5.1 // indirect
125126
github.com/josharian/intern v1.0.0 // indirect
126127
github.com/klauspost/compress v1.18.0 // indirect
127128
github.com/launchdarkly/ccache v1.1.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ github.com/itlightning/dateparse v0.2.0 h1:eOYLGZORnHweKdTZGOVjDXHhOwMQTNdP4g6+E
241241
github.com/itlightning/dateparse v0.2.0/go.mod h1:W2PH6/Sq+PuJJ6JUgx2nau+ew1KLGXwoGP1A240x204=
242242
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
243243
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
244+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
245+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
244246
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
245247
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
246248
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=

0 commit comments

Comments
 (0)