How to wire profiles.yaml into a Cobra CLI using Glazed middlewares (including proper profile-selection resolution).
For the conceptual overview (file format, precedence, config keys), see:
topics/15-profiles.md (“Profiles (profiles.yaml)”)This document focuses on the implementation pattern: where to insert the middleware and how to avoid the common “profile selection circularity” when profile selection is allowed to come from env/config.
Profile middleware in Pinocchio is responsible for loading and applying configuration fields from a specified profile. This middleware allows developers to define different configurations for various environments or use cases, which can be dynamically selected at runtime.
The profile middleware takes constructor arguments (defaultProfileFile, profileFile, profileName), so if you
compute those values before env/config are applied, you will capture defaults and load the wrong profile.
The correct approach is usually a bootstrap parse of profile-settings (and often command-settings) first, then
instantiate the profile middleware with the resolved values.
func GetCommandMiddlewares(
parsedCommandSections *values.Values,
cmd *cobra.Command,
args []string,
) ([]middlewares.Middleware, error) {
// 1) Bootstrap parse profile-settings (+ command-settings if config selection depends on it)
// using defaults + config + env + cobra, then read the resolved selection.
//
// 2) Build main chain:
// defaults -> profiles -> config -> env -> args -> flags
//
// (See `topics/15-profiles.md` for details.)
return []middlewares.Middleware{/* ... */}, nil
}
The recommended precedence is:
flags > env > config > profiles > defaults
Operationally, this usually means inserting GatherFlagsFromProfiles(...) above defaults but below config/env/flags.
For advanced use cases, combine profile middleware with additional config files using LoadFieldsFromFile or LoadFieldsFromFiles:
func GetCobraCommandGeppettoMiddlewares(
commandSettings *cli.GlazedCommandSettings,
cmd *cobra.Command,
args []string,
) ([]middlewares.Middleware, error) {
// ... existing profile setup ...
middlewares_ := []middlewares.Middleware{
// Command line arguments (highest priority)
sources.FromCobra(cmd),
sources.FromArgs(args),
// Profile-specific override files
sources.FromFile(
fmt.Sprintf("/etc/%s/overrides.yaml", commandSettings.Profile),
sources.WithSource("profile-overrides"),
),
// Profile configuration
profileMiddleware,
// ... rest of middleware chain ...
}
return middlewares_, nil
}
This pattern allows for:
When running the YOUR_PROGRAM command, a developer can specify a profile as follows:
YOUR_PROGRAM --profile development [command]
Or by setting an environment variable:
export YOUR_PROGRAM_PROFILE=development
YOUR_PROGRAM [command]
The middleware will then load the configuration fields from the development profile and apply them to the command.
You can use the custom profile middleware to load profiles from different sources:
// Load from a specific profile file
middlewares.GatherFlagsFromCustomProfiles(
"production",
middlewares.WithProfileFile("/etc/myapp/custom-profiles.yaml"),
middlewares.WithProfileParseOptions(sources.WithSource("custom-profiles")),
)
// Load from another app's profiles
middlewares.GatherFlagsFromCustomProfiles(
"shared-config",
middlewares.WithProfileAppName("central-config"),
middlewares.WithProfileParseOptions(sources.WithSource("shared-profiles")),
)
Load different configuration files based on the active profile:
// Load profile-specific config file
sources.FromFile(
fmt.Sprintf("/etc/myapp/%s.yaml", commandSettings.Profile),
sources.WithSource("profile-config"),
)
With profile files like:
/etc/myapp/development.yaml/etc/myapp/staging.yaml/etc/myapp/production.yamlCombine environment variables with custom config loading:
# Set profile and custom config path via environment
export YOUR_PROGRAM_PROFILE=production
export YOUR_PROGRAM_CUSTOM_CONFIG=/opt/configs/production-override.yaml
# The middleware can then use these environment variables
YOUR_PROGRAM [command]
customConfigPath := os.Getenv("YOUR_PROGRAM_CUSTOM_CONFIG")
if customConfigPath != "" {
middlewares_ = append(middlewares_,
sources.FromFile(
customConfigPath,
sources.WithSource("env-custom"),
),
)
}
A complete example showing how profiles and custom config work together:
func GetAdvancedMiddlewares(commandSettings *cli.GlazedCommandSettings) []middlewares.Middleware {
return []middlewares.Middleware{
// 1. Command line (highest priority)
sources.FromCobra(cmd),
// 2. Environment-specific overrides
sources.FromFile(
"/etc/myapp/local-overrides.yaml",
sources.WithSource("config"),
),
// 3. Profile-specific configuration
sources.FromFile(
fmt.Sprintf("/etc/myapp/profiles/%s.yaml", commandSettings.Profile),
sources.WithSource("config"),
),
// 4. Custom profile sources
middlewares.GatherFlagsFromCustomProfiles(
commandSettings.Profile,
middlewares.WithProfileFile("/etc/shared/organization-profiles.yaml"),
middlewares.WithProfileParseOptions(sources.WithSource("org-profiles")),
),
// 5. Profile middleware (from profiles.yaml)
middlewares.GatherFlagsFromProfiles(
defaultProfileFile,
commandSettings.ProfileFile,
commandSettings.Profile,
commandSettings.DefaultProfileName,
),
// 6. Shared organization config
// Provide explicit file paths or use a ConfigFiles resolver
// 7. Application defaults
sources.FromDefaults(),
}
}
This creates a configuration hierarchy where:
// Organization-wide profiles for all applications
middlewares.GatherFlagsFromCustomProfiles(
commandSettings.Profile,
middlewares.WithProfileAppName("org-config"),
middlewares.WithProfileParseOptions(sources.WithSource("org-profiles")),
)
// Team-specific profile overrides
middlewares.GatherFlagsFromCustomProfiles(
fmt.Sprintf("%s-%s", commandSettings.Team, commandSettings.Profile),
middlewares.WithProfileFile("/etc/team-configs/profiles.yaml"),
middlewares.WithProfileParseOptions(sources.WithSource("team-profiles")),
)
// Load different profile files based on deployment region
profileFile := fmt.Sprintf("/etc/regional-configs/%s/profiles.yaml", commandSettings.Region)
middlewares.GatherFlagsFromCustomProfiles(
commandSettings.Profile,
middlewares.WithProfileFile(profileFile),
middlewares.WithProfileRequired(true), // Must exist for regional deployments
)
// Base organizational profiles
middlewares.GatherFlagsFromCustomProfiles(
"base",
middlewares.WithProfileAppName("org-base-config"),
)
// Environment-specific profiles (inherits from base)
middlewares.GatherFlagsFromCustomProfiles(
commandSettings.Profile,
middlewares.WithProfileAppName("org-env-config"),
)
// Application-specific profiles (highest precedence)
middlewares.GatherFlagsFromProfiles(
defaultProfileFile,
commandSettings.ProfileFile,
commandSettings.Profile,
commandSettings.DefaultProfileName,
)