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>
51 lines
1.5 KiB
Go
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"
|
|
}
|