From a4e6eb568640a2f3df9e4f5994199649c7611758 Mon Sep 17 00:00:00 2001 From: Mofei Zhu <13761509829@163.com> Date: Thu, 24 Sep 2026 15:19:28 +0300 Subject: [PATCH] Add a persisted telemetry setting to mapbox config mapbox config set telemetry off stops the run's telemetry event in every shell, beside MAPBOX_CLI_NO_TELEMETRY for one. The setting is read again at exit, so the run that turns it off does not report itself. --- CHANGELOG.md | 4 ++++ README.md | 3 ++- docs/commands.md | 12 +++++++++--- src/config.rs | 14 +++++++++++++- src/telemetry_event.rs | 7 +++++-- tests/config.rs | 9 ++++++--- tests/telemetry_events.rs | 10 +++++++++- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f4b0fc..301252f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ that may never merge. They are not releases and are not listed here. code; it does find a `~/.mapbox/history` directory it didn't before, and `mapbox config list` now reports a second key, `history`. +- `mapbox config set telemetry off` turns the telemetry event off for good, + in every shell, the way `MAPBOX_CLI_NO_TELEMETRY=1` does for one. The run + that turns it off records nothing either. `mapbox config list` now also + reports `telemetry`. - Each run records one `cli.command` telemetry event, appended to `~/.mapbox/.telemetry/.jsonl` and kept for 7 days. Nothing is sent anywhere by default. It never touches stdout, the exit code or how long a diff --git a/README.md b/README.md index 9ad16d2..9d4efcf 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,8 @@ Each run also appends one event to `~/.mapbox/.telemetry/.jsonl` (kept for 7 days): the command's name, its options (a value only when it comes from a fixed list, otherwise just its length or size), how it ended, and how long it took — never a token, a file path or free text you typed. -It stays on this machine. `MAPBOX_CLI_NO_TELEMETRY=1` turns it off. +It stays on this machine. `MAPBOX_CLI_NO_TELEMETRY=1` or +`mapbox config set telemetry off` turns it off. ### Agent skills diff --git a/docs/commands.md b/docs/commands.md index cbeb1bd..8c0e439 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -3205,6 +3205,7 @@ was set in, and stays in every future shell instead. | --- | --- | --- | | `update-check` | `on` | The update notice; mirrors `MAPBOX_NO_UPDATE_CHECK` (see [Update notices](../README.md#update-notices)) | | `history` | `on` | [Command history](../README.md#command-history), read by `mapbox history`; `MAPBOX_HISTORY=0` or `=1` overrides it for a session | +| `telemetry` | `on` | The run's telemetry event; mirrors `MAPBOX_CLI_NO_TELEMETRY` (see [Privacy](../README.md#privacy)) | ### `mapbox config get` @@ -3216,7 +3217,7 @@ than failing, the same forgiving read the update-check cache itself uses. | Parameter | Effect | | --- | --- | -| `` | Which setting to read: `update-check` or `history`. | +| `` | Which setting to read: `update-check`, `history` or `telemetry`. | #### Examples @@ -3255,7 +3256,7 @@ without an environment variable. | Parameter | Effect | | --- | --- | -| `` | Which setting to change: `update-check` or `history`. | +| `` | Which setting to change: `update-check`, `history` or `telemetry`. | | `` | `on` or `off`. | #### Examples @@ -3313,6 +3314,7 @@ mapbox config list ``` update-check on history on +telemetry on ``` @@ -3326,6 +3328,10 @@ history on { "key": "history", "value": true + }, + { + "key": "telemetry", + "value": true } ] ``` @@ -3344,7 +3350,7 @@ default, a key explicitly set to the old default value does not. | Parameter | Effect | | --- | --- | -| `` | Which setting to clear: `update-check` or `history`. | +| `` | Which setting to clear: `update-check`, `history` or `telemetry`. | #### Examples diff --git a/src/config.rs b/src/config.rs index a33d479..0bf96ec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,7 +31,8 @@ const CONFIG_FILE: &str = "config.json"; const UPDATE_CHECK_KEY: &str = "update-check"; const HISTORY_KEY: &str = "history"; -const KEYS: &[&str] = &[UPDATE_CHECK_KEY, HISTORY_KEY]; +const TELEMETRY_KEY: &str = "telemetry"; +const KEYS: &[&str] = &[UPDATE_CHECK_KEY, HISTORY_KEY, TELEMETRY_KEY]; const ON: &str = "on"; const OFF: &str = "off"; @@ -46,6 +47,8 @@ struct Config { update_check: Option, #[serde(default, skip_serializing_if = "Option::is_none")] history: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + telemetry: Option, } fn config_path() -> Option { @@ -92,6 +95,12 @@ pub fn history_enabled() -> bool { read_config().history.unwrap_or(true) } +/// Whether the run's telemetry event may be recorded, per the persisted +/// setting. [`crate::telemetry_event`] checks it alongside `MAPBOX_CLI_NO_TELEMETRY`. +pub fn telemetry_enabled() -> bool { + read_config().telemetry.unwrap_or(true) +} + fn on_off(enabled: bool) -> &'static str { if enabled { ON @@ -107,6 +116,7 @@ fn resolve(config: &Config, key: &str) -> bool { match key { UPDATE_CHECK_KEY => update_check_setting(config), HISTORY_KEY => config.history.unwrap_or(true), + TELEMETRY_KEY => config.telemetry.unwrap_or(true), _ => unreachable!("clap's value_parser restricts `key` to {KEYS:?}"), } } @@ -120,6 +130,7 @@ fn clear(config: &mut Config, key: &str) { match key { UPDATE_CHECK_KEY => config.update_check = None, HISTORY_KEY => config.history = None, + TELEMETRY_KEY => config.telemetry = None, _ => unreachable!("clap's value_parser restricts `key` to {KEYS:?}"), } } @@ -185,6 +196,7 @@ pub fn set(matches: &ArgMatches, mode: Mode) -> Result<()> { match key.as_str() { UPDATE_CHECK_KEY => config.update_check = Some(enabled), HISTORY_KEY => config.history = Some(enabled), + TELEMETRY_KEY => config.telemetry = Some(enabled), _ => unreachable!("clap's value_parser restricts `key` to {KEYS:?}"), } write_config(&config)?; diff --git a/src/telemetry_event.rs b/src/telemetry_event.rs index 3828d12..d34db11 100644 --- a/src/telemetry_event.rs +++ b/src/telemetry_event.rs @@ -16,7 +16,8 @@ //! //! Best-effort throughout: nothing here can change a command's output, its //! exit code, or how long it takes to return. With telemetry off -//! (`MAPBOX_CLI_NO_TELEMETRY`), nothing is built or written. +//! (`MAPBOX_CLI_NO_TELEMETRY`, or `mapbox config set telemetry off`), +//! nothing is built or written. use std::io::IsTerminal; use std::path::Path; @@ -297,7 +298,9 @@ fn with_workflow(f: impl FnOnce(&mut Option)) { /// Builds the run's event and hands it to the sink, unless telemetry is off. pub(crate) fn deliver(record: &Record) { - if !telemetry::telemetry_allowed() { + // Read at the end of the run: the run that turns telemetry off is one + // that should not report itself. + if !telemetry::telemetry_allowed() || !crate::config::telemetry_enabled() { return; } let event = build(record); diff --git a/tests/config.rs b/tests/config.rs index a1ec7c4..a233ddd 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -135,7 +135,7 @@ fn list_reports_every_setting_including_an_unset_one() { assert!(empty.status.success()); assert_eq!( stdout(&empty), - r#"[{"key":"update-check","value":true},{"key":"history","value":true}]"# + r#"[{"key":"update-check","value":true},{"key":"history","value":true},{"key":"telemetry","value":true}]"# ); let set = command(&home) @@ -151,7 +151,7 @@ fn list_reports_every_setting_including_an_unset_one() { assert!(after.status.success()); assert_eq!( stdout(&after), - r#"[{"key":"update-check","value":false},{"key":"history","value":true}]"# + r#"[{"key":"update-check","value":false},{"key":"history","value":true},{"key":"telemetry","value":true}]"# ); let text = command(&home) @@ -159,7 +159,10 @@ fn list_reports_every_setting_including_an_unset_one() { .output() .expect("run mapbox config list"); assert!(text.status.success()); - assert_eq!(stdout(&text), "update-check\toff\nhistory\ton"); + assert_eq!( + stdout(&text), + "update-check\toff\nhistory\ton\ntelemetry\ton" + ); } #[test] diff --git a/tests/telemetry_events.rs b/tests/telemetry_events.rs index 2b28cf3..fd548ce 100644 --- a/tests/telemetry_events.rs +++ b/tests/telemetry_events.rs @@ -197,7 +197,7 @@ fn help_version_and_usage_errors_record_their_invocation() { } #[test] -fn the_opt_out_records_nothing() { +fn either_opt_out_records_nothing() { let home = scratch("opt-out-env"); let out = command(&home) .env("MAPBOX_CLI_NO_TELEMETRY", "1") @@ -209,6 +209,14 @@ fn the_opt_out_records_nothing() { !config_dir(&home).join(".telemetry").exists(), "MAPBOX_CLI_NO_TELEMETRY=1 still wrote telemetry" ); + + let home = scratch("opt-out-config"); + assert!(run(&home, &["config", "set", "telemetry", "off"]) + .status + .success()); + // That run records nothing either: the setting it wrote is read at exit. + let _ = run(&home, &["config", "list"]); + assert_eq!(events(&home), Vec::::new()); } #[test]