feat: add railway telemetry command (enable/disable/status) (#802)

Adds a user-friendly CLI command to manage telemetry preferences,
stored persistently in ~/.railway/preferences.json. Previously the
only way to opt out was via environment variables.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Faraz Patankar 2026-02-23 21:46:22 +04:00 committed by GitHub
parent a8a5afe145
commit dc16439423
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 92 additions and 2 deletions

View file

@ -37,6 +37,7 @@ pub mod shell;
pub mod ssh;
pub mod starship;
pub mod status;
pub mod telemetry_cmd;
pub mod unlink;
pub mod up;
pub mod upgrade;

View file

@ -0,0 +1,56 @@
use super::*;
use crate::telemetry::{Preferences, is_telemetry_disabled_by_env};
/// Manage telemetry preferences
#[derive(Parser)]
pub struct Args {
#[clap(subcommand)]
command: Commands,
}
#[derive(Parser)]
enum Commands {
/// Enable telemetry data collection
Enable,
/// Disable telemetry data collection
Disable,
/// Show current telemetry status
Status,
}
pub async fn command(args: Args) -> Result<()> {
match args.command {
Commands::Enable => {
let mut prefs = Preferences::read();
prefs.telemetry_disabled = false;
prefs.write();
println!("{}", "Telemetry enabled.".green());
}
Commands::Disable => {
let mut prefs = Preferences::read();
prefs.telemetry_disabled = true;
prefs.write();
println!("{}", "Telemetry disabled.".yellow());
}
Commands::Status => {
let prefs = Preferences::read();
let env_disabled = is_telemetry_disabled_by_env();
if env_disabled {
println!(
"Telemetry is {} (disabled by environment variable)",
"disabled".yellow()
);
} else if prefs.telemetry_disabled {
println!(
"Telemetry is {} (disabled via {})",
"disabled".yellow(),
"railway telemetry disable".bold()
);
} else {
println!("Telemetry is {}", "enabled".green());
}
}
}
Ok(())
}

View file

@ -51,6 +51,7 @@ commands!(
ssh,
starship,
status,
telemetry_cmd(telemetry),
unlink,
up,
upgrade,

View file

@ -42,7 +42,7 @@ pub fn show_notice_if_needed() {
}
eprintln!(
"{}\nYou can opt out by setting RAILWAY_NO_TELEMETRY=1 or DO_NOT_TRACK=1 in your environment.\n{}",
"{}\nYou can opt out by running `railway telemetry disable` or by setting RAILWAY_NO_TELEMETRY=1 in your environment.\n{}",
"Railway now collects CLI usage data to improve the developer experience.".bold(),
format!("Learn more: {}", "https://docs.railway.com/cli/telemetry").dimmed(),
);
@ -71,10 +71,42 @@ fn env_var_is_truthy(name: &str) -> bool {
.unwrap_or(false)
}
fn is_telemetry_disabled() -> bool {
#[derive(serde::Serialize, serde::Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Preferences {
#[serde(default)]
pub telemetry_disabled: bool,
}
impl Preferences {
fn path() -> Option<std::path::PathBuf> {
dirs::home_dir().map(|h| h.join(".railway/preferences.json"))
}
pub fn read() -> Self {
Self::path()
.and_then(|p| std::fs::read_to_string(p).ok())
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}
pub fn write(&self) {
if let Some(path) = Self::path() {
let _ = serde_json::to_string(self)
.ok()
.map(|contents| std::fs::write(path, contents));
}
}
}
pub fn is_telemetry_disabled_by_env() -> bool {
env_var_is_truthy("DO_NOT_TRACK") || env_var_is_truthy("RAILWAY_NO_TELEMETRY")
}
fn is_telemetry_disabled() -> bool {
is_telemetry_disabled_by_env() || Preferences::read().telemetry_disabled
}
pub async fn send(event: CliTrackEvent) {
if is_telemetry_disabled() {
return;