rdev/internal/envutil/envutil.go
jordan 8282d60c69 feat: implement composable monorepo template system with component architecture
Adds the composable monorepo template system that generates project skeletons
with pluggable components (service, worker, app-react, app-astro, cli).

Key changes:
- Monorepo skeleton templates with shared pkg/, scripts/, and git hooks
- Component templates (service, worker, app-react, app-astro, cli) with
  Dockerfiles, CI steps, and component.yaml manifests
- Component domain model with validation and dependency resolution
- Component handler endpoints for CRUD and composition
- Template provider extended with BuildComposableProject and component assembly
- Deployer extended with composable project deployment support
- Handler timeout constants (TimeoutFastLookup through TimeoutLongRunning)
- envutil package for centralized env var reads with defaults
- api.DecodeJSON helper for standardized request body decoding
- Standardized response helpers (WriteBadRequest, WriteNotFound, etc.)
- Replaced fullstack-app cookbook with composable-app cookbook
- Hardened handler timeouts, logging, and error responses across all handlers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 19:11:42 -07:00

51 lines
1.5 KiB
Go

// Package envutil provides environment variable helpers with defaults.
//
// rdev uses os.Getenv() directly rather than Viper because:
// - rdev always runs in Kubernetes with env vars injected via ConfigMaps/Secrets
// - rdev has a credential store overlay (loadInfraConfig) that Viper can't model
// - Adding Viper would add complexity without functional benefit for this use case
//
// Generated projects (skeleton templates) use Viper because:
// - App services benefit from .env file loading during development
// - Viper provides built-in duration parsing, type coercion, and defaults
// - App config is simpler (no credential store overlay)
//
// This divergence is intentional. See: .claude/guides/services/templates.md
package envutil
import (
"os"
"strconv"
"strings"
)
// GetEnv returns the environment variable value or the default.
func GetEnv(key, defaultVal string) string {
if v := os.Getenv(key); v != "" {
return v
}
return defaultVal
}
// GetEnvInt returns the environment variable as an int or the default.
func GetEnvInt(key string, defaultVal int) int {
v := os.Getenv(key)
if v == "" {
return defaultVal
}
if i, err := strconv.Atoi(v); err == nil {
return i
}
return defaultVal
}
// GetEnvBool returns the environment variable as a bool or the default.
func GetEnvBool(key string, defaultVal bool) bool {
v := os.Getenv(key)
if v == "" {
return defaultVal
}
v = strings.ToLower(v)
return v == "true" || v == "1" || v == "yes"
}