| 1 | // Package telemetry collects and uploads bounded, content-free CLI counters. |
| 2 | package telemetry |
| 3 | |
| 4 | import ( |
| 5 | "os" |
| 6 | "regexp" |
| 7 | "strings" |
| 8 | ) |
| 9 | |
| 10 | var releaseVersionPattern = regexp.MustCompile(`^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z._-]+)?$`) |
| 11 | |
| 12 | // Enabled applies the fail-closed CLI telemetry policy. Explicit "on" permits |
| 13 | // local headless runs, but never overrides CI, development builds, or |
| 14 | // environment opt-outs. |
| 15 | func Enabled(mode, version string, interactive bool) bool { |
| 16 | mode = strings.ToLower(strings.TrimSpace(mode)) |
| 17 | if mode == "off" || !releaseVersionPattern.MatchString(strings.TrimSpace(version)) { |
| 18 | return false |
| 19 | } |
| 20 | if envOptOut() || inCI() { |
| 21 | return false |
| 22 | } |
| 23 | if mode == "on" { |
| 24 | return true |
| 25 | } |
| 26 | return interactive |
| 27 | } |
| 28 | |
| 29 | func envOptOut() bool { |
| 30 | if strings.TrimSpace(os.Getenv("DO_NOT_TRACK")) != "" { |
| 31 | return true |
| 32 | } |
| 33 | switch strings.ToLower(strings.TrimSpace(os.Getenv("REASONIX_TELEMETRY"))) { |
| 34 | case "0", "false", "off", "no": |
| 35 | return true |
| 36 | default: |
| 37 | return false |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | func inCI() bool { |
| 42 | for _, key := range []string{ |
| 43 | "CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "GITLAB_CI", |
| 44 | "BUILDKITE", "CIRCLECI", "JENKINS_URL", "TEAMCITY_VERSION", "TF_BUILD", |
| 45 | } { |
| 46 | if strings.TrimSpace(os.Getenv(key)) != "" { |
| 47 | return true |
| 48 | } |
| 49 | } |
| 50 | return false |
| 51 | } |
| 52 |