Add config support for disabling telemetry (#226)

* Add config-driven telemetry disable and surface telemetry status

* Update config/config_template_test.go

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>

---------

Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Sahil Bansal
2026-04-22 22:04:56 +05:30
committed by GitHub
co-authored by devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 544b38b278
commit 996d9aeca0
9 changed files with 45 additions and 2 deletions
+3 -1
View File
@@ -225,4 +225,6 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gui
## Telemetry
PMG collects anonymous usage data to improve project stability and reliability.
To disable: `export PMG_DISABLE_TELEMETRY=true`.
To disable, either:
- Set `disable_telemetry: true` in your PMG config file, or
- Export `PMG_DISABLE_TELEMETRY=true`.
+1 -1
View File
@@ -31,7 +31,7 @@ func runSync(cmd *cobra.Command, args []string) error {
cfg := config.Get()
if analytics.IsDisabled() {
ui.Infof("Cloud sync is disabled because telemetry is disabled (PMG_DISABLE_TELEMETRY)")
ui.Infof("Cloud sync is disabled because telemetry is disabled (disable_telemetry or PMG_DISABLE_TELEMETRY)")
return nil
}
+3
View File
@@ -8,6 +8,7 @@ import (
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/internal/alias"
"github.com/safedep/pmg/internal/analytics"
"github.com/safedep/pmg/internal/ui"
"github.com/safedep/pmg/internal/version"
"github.com/spf13/cobra"
@@ -38,6 +39,7 @@ func executeSetupInfo() error {
configEntries := make(map[string]string)
configEntries["Config File"] = cfg.ConfigFilePath()
configEntries["Proxy Mode"] = strconv.FormatBool(cfg.IsProxyModeEnabled())
configEntries["Proxy Install Only"] = strconv.FormatBool(cfg.Config.ProxyInstallOnly)
ui.PrintInfoSection("Configuration", configEntries)
// Shell Integration section
@@ -89,6 +91,7 @@ func executeSetupInfo() error {
securityEntries["Dependency Cooldown"] = strconv.FormatBool(cfg.Config.DependencyCooldown.Enabled)
securityEntries["Dependency Cooldown Days"] = strconv.Itoa(cfg.Config.DependencyCooldown.Days)
securityEntries["Telemetry"] = strconv.FormatBool(!analytics.IsDisabled())
securityEntries["Event Logging"] = strconv.FormatBool(!cfg.Config.SkipEventLogging)
securityEntries["Event Log Directory"] = cfg.EventLogDir()
+4
View File
@@ -53,6 +53,9 @@ type Config struct {
// Paranoid enables high-security defaults (e.g., treating suspicious behavior as malicious).
Paranoid bool `mapstructure:"paranoid"`
// DisableTelemetry allows turning off telemetry collection.
DisableTelemetry bool `mapstructure:"disable_telemetry"`
// TrustedPackages allows for trusting a suspicious package and ignoring the suspicious behaviour for the package in future installations
TrustedPackages []TrustedPackage `mapstructure:"trusted_packages"`
@@ -242,6 +245,7 @@ func DefaultConfig() RuntimeConfig {
TransitiveDepth: 5,
IncludeDevDependencies: false,
Paranoid: false,
DisableTelemetry: false,
EventLogRetentionDays: 7,
SkipEventLogging: false,
ExperimentalProxyMode: false,
+3
View File
@@ -20,6 +20,9 @@ verbosity: normal
# as malicious packages
paranoid: false
# Disable anonymous telemetry. Default is false.
disable_telemetry: false
# Skip event logging. Default is false.
# When skip_event_logging is false, all events will be logged to file. These events are useful for audit
# trail and incident response on systems using PMG. Set this config to true to disable event logging.
+2
View File
@@ -30,6 +30,7 @@ func TestTemplateParsesAsYAML(t *testing.T) {
assert.Equal(t, 5, cfg.TransitiveDepth, "expected TransitiveDepth 5")
assert.False(t, false, cfg.IncludeDevDependencies, "expected IncludeDevDependencies false")
assert.False(t, false, cfg.Paranoid, "expected Paranoid false")
assert.False(t, cfg.DisableTelemetry, "expected DisableTelemetry false")
assert.False(t, false, cfg.SkipEventLogging, "expected SkipEventLogging false")
assert.Equal(t, 7, cfg.EventLogRetentionDays, "expected EventLogRetentionDays 7")
assert.Len(t, cfg.TrustedPackages, 1)
@@ -52,6 +53,7 @@ func TestTemplateMatchesDefaults(t *testing.T) {
assert.Equal(t, def.TransitiveDepth, parsed.TransitiveDepth, "transitive_depth mismatch")
assert.Equal(t, def.IncludeDevDependencies, parsed.IncludeDevDependencies, "include_dev_dependencies mismatch")
assert.Equal(t, def.Paranoid, parsed.Paranoid, "paranoid mismatch")
assert.Equal(t, def.DisableTelemetry, parsed.DisableTelemetry, "disable_telemetry mismatch")
assert.Equal(t, def.SkipEventLogging, parsed.SkipEventLogging, "skip_event_logging mismatch")
assert.Equal(t, def.EventLogRetentionDays, parsed.EventLogRetentionDays, "event_log_retention_days mismatch")
assert.Equal(t, def.Verbosity, parsed.Verbosity, "verbosity mismatch")
+13
View File
@@ -154,6 +154,19 @@ func TestConfigPrecedence(t *testing.T) {
assert.Equal(t, true, Get().Config.ProxyInstallOnly, "config file should override default")
})
t.Run("telemetry can be disabled via config", func(t *testing.T) {
tmpDir := t.TempDir()
t.Setenv("PMG_CONFIG_DIR", tmpDir)
t.Setenv("PMG_DISABLE_TELEMETRY", "")
configPath := filepath.Join(tmpDir, "config.yml")
err := os.WriteFile(configPath, []byte("disable_telemetry: true\n"), 0o644)
require.NoError(t, err)
initConfig()
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)
+5
View File
@@ -8,6 +8,7 @@ import (
"github.com/google/uuid"
"github.com/posthog/posthog-go"
"github.com/safedep/pmg/config"
)
const (
@@ -47,6 +48,10 @@ func init() {
}
func isTelemetryDisabled() bool {
if config.Get().Config.DisableTelemetry {
return true
}
val := os.Getenv(telemetryDisableEnvKey)
if booleanVal, err := strconv.ParseBool(val); err == nil {
return booleanVal
+11
View File
@@ -4,6 +4,7 @@ import (
"os"
"testing"
"github.com/safedep/pmg/config"
"github.com/stretchr/testify/assert"
)
@@ -16,8 +17,18 @@ func TestIsDisabled(t *testing.T) {
})
t.Run("returns false if PMG_DISABLE_TELEMETRY is not set", func(t *testing.T) {
config.Get().Config.DisableTelemetry = false
assert.False(t, IsDisabled())
})
t.Run("returns true if telemetry is disabled in config", func(t *testing.T) {
config.Get().Config.DisableTelemetry = true
t.Cleanup(func() {
config.Get().Config.DisableTelemetry = false
})
assert.True(t, IsDisabled())
})
}
func TestCloseIsImmutable(t *testing.T) {