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
+102 -29
View File
@@ -28,7 +28,7 @@ func TestConfigHasDefaultValues(t *testing.T) {
assert.Len(t, config.Config.TrustedPackages, 1)
assert.Equal(t, "/tmp/pmg-test/random-does-not-exist", config.configDir)
assert.Equal(t, "/tmp/pmg-test/random-does-not-exist/config.yml", config.configFilePath)
assert.Equal(t, false, config.Config.ProxyInstallOnly)
assert.Equal(t, false, config.Config.Proxy.InstallOnly)
})
t.Run("when no config directory is set", func(t *testing.T) {
@@ -69,7 +69,7 @@ func TestPartialConfigFallsBackToDefaults(t *testing.T) {
// Missing keys should fall back to DefaultConfig() values, not Go zero values
defaults := DefaultConfig().Config
assert.Equal(t, defaults.TransitiveDepth, config.Config.TransitiveDepth)
assert.Equal(t, defaults.ProxyMode, config.Config.ProxyMode)
assert.Equal(t, defaults.Proxy.Enabled, config.Config.Proxy.Enabled)
assert.Equal(t, defaults.Verbosity, config.Config.Verbosity)
assert.Equal(t, defaults.EventLogRetentionDays, config.Config.EventLogRetentionDays)
assert.Equal(t, defaults.DependencyCooldown.Enabled, config.Config.DependencyCooldown.Enabled)
@@ -102,14 +102,14 @@ func TestPartialConfigWithNestedOverride(t *testing.T) {
// Top-level fields should fall back to defaults
assert.Equal(t, defaults.Transitive, config.Config.Transitive)
assert.Equal(t, defaults.TransitiveDepth, config.Config.TransitiveDepth)
assert.Equal(t, defaults.ProxyMode, config.Config.ProxyMode)
assert.Equal(t, defaults.Proxy.Enabled, config.Config.Proxy.Enabled)
}
func TestProxyInstallOnlyConfig(t *testing.T) {
t.Run("defaults to false", func(t *testing.T) {
t.Setenv("PMG_CONFIG_DIR", "/tmp/pmg-test/random-does-not-exist")
initConfig()
assert.Equal(t, false, Get().Config.ProxyInstallOnly)
assert.Equal(t, false, Get().Config.Proxy.InstallOnly)
})
t.Run("can be set to true via config file", func(t *testing.T) {
@@ -117,18 +117,18 @@ func TestProxyInstallOnlyConfig(t *testing.T) {
t.Setenv("PMG_CONFIG_DIR", tmpDir)
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte("proxy_install_only: true\n"), 0o644)
err := os.WriteFile(configPath, []byte("proxy:\n install_only: true\n"), 0o644)
require.NoError(t, err)
initConfig()
assert.Equal(t, true, Get().Config.ProxyInstallOnly)
assert.Equal(t, true, Get().Config.Proxy.InstallOnly)
})
}
// TestConfigPrecedence verifies the expected override order:
// flags > env var > config file > default
func TestConfigPrecedence(t *testing.T) {
t.Run("env var overrides config file", func(t *testing.T) {
t.Run("env var sets legacy flat field", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
t.Setenv("PMG_PROXY_INSTALL_ONLY", "true")
@@ -138,7 +138,8 @@ func TestConfigPrecedence(t *testing.T) {
require.NoError(t, err)
initConfig()
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "env var should override config file")
// PMG_PROXY_INSTALL_ONLY maps to the flat proxy_install_only key, not nested proxy.install_only
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "env var should set legacy flat field")
})
t.Run("config file overrides default", func(t *testing.T) {
@@ -147,11 +148,11 @@ func TestConfigPrecedence(t *testing.T) {
t.Setenv("PMG_PROXY_INSTALL_ONLY", "")
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte("proxy_install_only: true\n"), 0o644)
err := os.WriteFile(configPath, []byte("proxy:\n install_only: true\n"), 0o644)
require.NoError(t, err)
initConfig()
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "config file should override default")
assert.Equal(t, true, Get().Config.Proxy.InstallOnly, "config file should override default")
})
t.Run("telemetry can be disabled via config", func(t *testing.T) {
@@ -167,27 +168,12 @@ func TestConfigPrecedence(t *testing.T) {
assert.Equal(t, true, Get().Config.DisableTelemetry, "config file should disable telemetry")
})
t.Run("env var works when key is absent from config file", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
t.Setenv("PMG_PROXY_INSTALL_ONLY", "true")
// Config file exists but proxy_install_only is not in it (e.g. commented out
// or user hasn't re-run "pmg setup install" after an upgrade)
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte("transitive: false\n"), 0o644)
require.NoError(t, err)
initConfig()
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "env var should work even when key is absent from config file")
})
t.Run("env var works without a config file", func(t *testing.T) {
t.Setenv("PMG_CONFIG_DIR", "/tmp/pmg-test/random-does-not-exist")
t.Setenv("PMG_PROXY_INSTALL_ONLY", "true")
t.Setenv("PMG_PARANOID", "true")
initConfig()
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "env var should work even without a config file")
assert.Equal(t, true, Get().Config.Paranoid, "env var should work even without a config file")
})
t.Run("default is used when neither env var nor config file sets the key", func(t *testing.T) {
@@ -200,7 +186,8 @@ func TestConfigPrecedence(t *testing.T) {
require.NoError(t, err)
initConfig()
assert.Equal(t, false, Get().Config.ProxyInstallOnly, "should use default when key absent from config and env")
assert.Equal(t, false, Get().Config.Proxy.InstallOnly, "should use default when key absent from config and env")
assert.Equal(t, false, Get().Config.ProxyInstallOnly, "legacy flat field should also default to false")
})
t.Run("cobra flag overrides env var", func(t *testing.T) {
@@ -308,7 +295,7 @@ func TestWriteTemplateConfigMergesExistingConfig(t *testing.T) {
assert.Contains(t, raw, "transitive_depth: 10")
// New keys from template added
assert.Contains(t, raw, "proxy_mode:")
assert.Contains(t, raw, "proxy:")
assert.Contains(t, raw, "sandbox:")
assert.Contains(t, raw, "verbosity:")
}
@@ -329,3 +316,89 @@ func TestWriteTemplateConfigCreatesNewFile(t *testing.T) {
// Should be the full template
assert.Equal(t, templateConfig, string(result))
}
func TestProxyConfigSection(t *testing.T) {
t.Run("defaults to enabled with install_only false", func(t *testing.T) {
t.Setenv("PMG_CONFIG_DIR", "/tmp/pmg-test/random-does-not-exist")
initConfig()
cfg := Get()
assert.Equal(t, true, cfg.Config.Proxy.Enabled)
assert.Equal(t, false, cfg.Config.Proxy.InstallOnly)
assert.NotNil(t, cfg.Config.Proxy.Policies)
})
t.Run("reads proxy section from config file", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
configYAML := `proxy:
enabled: true
install_only: true
policies:
npm:
skip_commands: ["my-script", "dev"]
`
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte(configYAML), 0o644)
require.NoError(t, err)
initConfig()
cfg := Get()
assert.Equal(t, true, cfg.Config.Proxy.Enabled)
assert.Equal(t, true, cfg.Config.Proxy.InstallOnly)
assert.Equal(t, []string{"my-script", "dev"}, cfg.Config.Proxy.Policies["npm"].SkipCommands)
})
t.Run("falls back to legacy keys from config file", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
configYAML := `proxy_mode: false
proxy_install_only: true
`
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte(configYAML), 0o644)
require.NoError(t, err)
initConfig()
cfg := Get()
assert.Equal(t, false, cfg.Config.Proxy.Enabled)
assert.Equal(t, true, cfg.Config.Proxy.InstallOnly)
})
t.Run("falls back to legacy keys from env vars", func(t *testing.T) {
t.Setenv("PMG_CONFIG_DIR", "/tmp/pmg-test/random-does-not-exist")
t.Setenv("PMG_PROXY_MODE", "false")
t.Setenv("PMG_PROXY_INSTALL_ONLY", "true")
initConfig()
cfg := Get()
assert.Equal(t, false, cfg.Config.Proxy.Enabled, "PMG_PROXY_MODE=false should set Proxy.Enabled=false")
assert.Equal(t, true, cfg.Config.Proxy.InstallOnly, "PMG_PROXY_INSTALL_ONLY=true should set Proxy.InstallOnly=true")
})
t.Run("new proxy section takes precedence over old keys", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
configYAML := `proxy_mode: false
proxy_install_only: true
proxy:
enabled: true
install_only: false
`
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte(configYAML), 0o644)
require.NoError(t, err)
initConfig()
cfg := Get()
assert.Equal(t, true, cfg.Config.Proxy.Enabled, "new proxy.enabled should win over old proxy_mode")
assert.Equal(t, false, cfg.Config.Proxy.InstallOnly, "new proxy.install_only should win over old proxy_install_only")
})
}