feat: consolidate proxy config into structured section and add support for custom commands to skip proxy (#240)

* feat: add ProxyConfig struct with per-PM skip_commands and legacy fallback

* feat: consolidate proxy config into structured section with backward compat

Replaces flat proxy_mode/proxy_install_only keys with a structured proxy
section supporting per-package-manager skip_commands. Legacy keys are
respected via fallback when user's config lacks the new proxy section.
Removes deprecated experimental_proxy_mode config and flag.

* fix: env var resolution for nested config keys and deduplicate skip command matching

- Add "." to "_" in Viper env key replacer so nested keys like
  sandbox.enabled resolve from PMG_SANDBOX_ENABLED (was silently broken)
- Export IsFirstNonFlagArgInList and remove duplicate from proxy_flow.go
- Add table-driven tests for skip command matching with real-world cases
- Remove redundant env var test

* docs: update proxy configuration and env var documentation

Update config.md env var table to reflect new proxy.enabled and
proxy.install_only keys. Add proxy configuration section to proxy.md
covering config structure, per-PM skip commands, CLI flags, and env vars.

* fix: legacy fallback precedence
This commit is contained in:
Sahil Bansal
2026-05-06 18:27:23 +05:30
committed by GitHub
parent d6755d3f44
commit d1dd2560a4
15 changed files with 423 additions and 71 deletions
+42 -1
View File
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
// loadViperConfig loads the configuration using Viper.
@@ -21,7 +22,7 @@ func loadViperConfig() error {
v.SetConfigType("yaml")
v.SetEnvPrefix("PMG")
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
// Load the embedded template as the base so Viper knows all keys and their
// defaults. This is required for AutomaticEnv to resolve PMG_* env vars for
@@ -44,5 +45,45 @@ func loadViperConfig() error {
}
globalConfig.Config = merged
// Resolve proxy config: new proxy section > legacy flat keys.
// Viper can't distinguish "value from template" vs "value from user config"
// (v.IsSet is always true for template keys), so we check the raw user file.
if !hasProxySectionInFile(configPath) {
applyProxyLegacyFallback(v)
}
return nil
}
// hasProxySectionInFile checks whether the user's config file contains a
// top-level "proxy" key. Returns false if the file doesn't exist or can't
// be parsed.
func hasProxySectionInFile(path string) bool {
data, err := os.ReadFile(path)
if err != nil {
return false
}
var raw map[string]any
if err := yaml.Unmarshal(data, &raw); err != nil {
return false
}
_, ok := raw["proxy"]
return ok
}
// applyProxyLegacyFallback populates the new Proxy struct from deprecated
// flat keys when the user's config file does not have a proxy: section.
// New env vars (PMG_PROXY_ENABLED, PMG_PROXY_INSTALL_ONLY) take precedence
// over legacy config file keys to respect the documented precedence order.
func applyProxyLegacyFallback(v *viper.Viper) {
if os.Getenv("PMG_PROXY_ENABLED") == "" && v.IsSet("proxy_mode") {
globalConfig.Config.Proxy.Enabled = v.GetBool("proxy_mode")
}
if os.Getenv("PMG_PROXY_INSTALL_ONLY") == "" && v.IsSet("proxy_install_only") {
globalConfig.Config.Proxy.InstallOnly = v.GetBool("proxy_install_only")
}
}