Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<date>.jsonl` and kept for 7 days. Nothing is sent
anywhere by default. It never touches stdout, the exit code or how long a
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ Each run also appends one event to `~/.mapbox/.telemetry/<date>.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

Expand Down
12 changes: 9 additions & 3 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -3216,7 +3217,7 @@ than failing, the same forgiving read the update-check cache itself uses.

| Parameter | Effect |
| --- | --- |
| `<key>` | Which setting to read: `update-check` or `history`. |
| `<key>` | Which setting to read: `update-check`, `history` or `telemetry`. |

#### Examples

Expand Down Expand Up @@ -3255,7 +3256,7 @@ without an environment variable.

| Parameter | Effect |
| --- | --- |
| `<key>` | Which setting to change: `update-check` or `history`. |
| `<key>` | Which setting to change: `update-check`, `history` or `telemetry`. |
| `<value>` | `on` or `off`. |

#### Examples
Expand Down Expand Up @@ -3313,6 +3314,7 @@ mapbox config list
```
update-check on
history on
telemetry on
```

</td><td>
Expand All @@ -3326,6 +3328,10 @@ history on
{
"key": "history",
"value": true
},
{
"key": "telemetry",
"value": true
}
]
```
Expand All @@ -3344,7 +3350,7 @@ default, a key explicitly set to the old default value does not.

| Parameter | Effect |
| --- | --- |
| `<key>` | Which setting to clear: `update-check` or `history`. |
| `<key>` | Which setting to clear: `update-check`, `history` or `telemetry`. |

#### Examples

Expand Down
14 changes: 13 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -46,6 +47,8 @@ struct Config {
update_check: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
history: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
telemetry: Option<bool>,
}

fn config_path() -> Option<PathBuf> {
Expand Down Expand Up @@ -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
Expand All @@ -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:?}"),
}
}
Expand All @@ -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:?}"),
}
}
Expand Down Expand Up @@ -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)?;
Expand Down
7 changes: 5 additions & 2 deletions src/telemetry_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -297,7 +298,9 @@ fn with_workflow(f: impl FnOnce(&mut Option<Workflow>)) {

/// 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);
Expand Down
9 changes: 6 additions & 3 deletions tests/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -151,15 +151,18 @@ 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)
.args(["-o", "text", "config", "list"])
.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]
Expand Down
10 changes: 9 additions & 1 deletion tests/telemetry_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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::<Value>::new());
}

#[test]
Expand Down
Loading