diff --git a/Cargo.lock b/Cargo.lock index 7a86cf72a7..f0f7fb512a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9025,6 +9025,7 @@ dependencies = [ "isahc", "lazy_static", "log", + "parking_lot 0.11.2", "rand 0.8.5", "rust-embed", "serde", diff --git a/assets/settings/default.json b/assets/settings/default.json index 01165c9e31..f81f2b0252 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -503,5 +503,28 @@ // } // } // } + }, + // The server to connect to. If the environment variable + // ZED_SERVER_URL is set, it will override this setting. + "server_url": "https://zed.dev", + // Settings overrides to use when using Zed Preview. + // Mostly useful for developers who are managing multiple instances of Zed. + "preview": { + // "theme": "Andromeda" + }, + // Settings overrides to use when using Zed Nightly. + // Mostly useful for developers who are managing multiple instances of Zed. + "nightly": { + // "theme": "Andromeda" + }, + // Settings overrides to use when using Zed Stable. + // Mostly useful for developers who are managing multiple instances of Zed. + "stable": { + // "theme": "Andromeda" + }, + // Settings overrides to use when using Zed Stable. + // Mostly useful for developers who are managing multiple instances of Zed. + "dev": { + // "theme": "Andromeda" } } diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 5ce3316be8..9673e8bbb2 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -25,8 +25,11 @@ use std::{ time::Duration, }; use update_notification::UpdateNotification; -use util::channel::{AppCommitSha, ReleaseChannel}; use util::http::HttpClient; +use util::{ + channel::{AppCommitSha, ReleaseChannel}, + http::ZedHttpClient, +}; use workspace::Workspace; const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification"; @@ -54,9 +57,8 @@ pub enum AutoUpdateStatus { pub struct AutoUpdater { status: AutoUpdateStatus, current_version: SemanticVersion, - http_client: Arc, + http_client: Arc, pending_poll: Option>>, - server_url: String, } #[derive(Deserialize)] @@ -92,7 +94,7 @@ impl Settings for AutoUpdateSetting { } } -pub fn init(http_client: Arc, server_url: String, cx: &mut AppContext) { +pub fn init(http_client: Arc, cx: &mut AppContext) { AutoUpdateSetting::register(cx); cx.observe_new_views(|workspace: &mut Workspace, _cx| { @@ -106,7 +108,7 @@ pub fn init(http_client: Arc, server_url: String, cx: &mut AppCo if let Some(version) = ZED_APP_VERSION.or_else(|| cx.app_metadata().app_version) { let auto_updater = cx.new_model(|cx| { - let updater = AutoUpdater::new(version, http_client, server_url); + let updater = AutoUpdater::new(version, http_client); let mut update_subscription = AutoUpdateSetting::get_global(cx) .0 @@ -151,10 +153,11 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<( ReleaseChannel::Stable | ReleaseChannel::Preview ) { let auto_updater = auto_updater.read(cx); - let server_url = &auto_updater.server_url; let release_channel = release_channel.dev_name(); let current_version = auto_updater.current_version; - let url = format!("{server_url}/releases/{release_channel}/{current_version}"); + let url = &auto_updater + .http_client + .zed_url(&format!("/releases/{release_channel}/{current_version}")); cx.open_url(&url); } @@ -191,16 +194,11 @@ impl AutoUpdater { cx.default_global::>>().clone() } - fn new( - current_version: SemanticVersion, - http_client: Arc, - server_url: String, - ) -> Self { + fn new(current_version: SemanticVersion, http_client: Arc) -> Self { Self { status: AutoUpdateStatus::Idle, current_version, http_client, - server_url, pending_poll: None, } } @@ -246,18 +244,14 @@ impl AutoUpdater { } async fn update(this: Model, mut cx: AsyncAppContext) -> Result<()> { - let (client, server_url, current_version) = this.read_with(&cx, |this, _| { - ( - this.http_client.clone(), - this.server_url.clone(), - this.current_version, - ) + let (client, current_version) = this.read_with(&cx, |this, _| { + (this.http_client.clone(), this.current_version) })?; - let mut url_string = format!( - "{server_url}/api/releases/latest?asset=Zed.dmg&os={}&arch={}", + let mut url_string = client.zed_url(&format!( + "/api/releases/latest?asset=Zed.dmg&os={}&arch={}", OS, ARCH - ); + )); cx.update(|cx| { if let Some(param) = cx .try_global::() diff --git a/crates/channel/src/channel_store_tests.rs b/crates/channel/src/channel_store_tests.rs index ad2098766d..6450736d34 100644 --- a/crates/channel/src/channel_store_tests.rs +++ b/crates/channel/src/channel_store_tests.rs @@ -329,6 +329,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) { fn init_test(cx: &mut AppContext) -> Model { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); + client::init_settings(cx); let http = FakeHttpClient::with_404_response(); let client = Client::new(http.clone(), cx); diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 3c2a831ce2..dd9514ac56 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -26,7 +26,7 @@ use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, Requ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json; -use settings::Settings; +use settings::{Settings, SettingsStore}; use std::{ any::TypeId, collections::HashMap, @@ -41,8 +41,8 @@ use std::{ use telemetry::Telemetry; use thiserror::Error; use url::Url; -use util::channel::ReleaseChannel; use util::http::HttpClient; +use util::{channel::ReleaseChannel, http::ZedHttpClient}; use util::{ResultExt, TryFutureExt}; pub use rpc::*; @@ -50,9 +50,8 @@ pub use telemetry::Event; pub use user::*; lazy_static! { - pub static ref ZED_SERVER_URL: String = - std::env::var("ZED_SERVER_URL").unwrap_or_else(|_| "https://zed.dev".to_string()); - pub static ref ZED_RPC_URL: Option = std::env::var("ZED_RPC_URL").ok(); + static ref ZED_SERVER_URL: Option = std::env::var("ZED_SERVER_URL").ok(); + static ref ZED_RPC_URL: Option = std::env::var("ZED_RPC_URL").ok(); pub static ref IMPERSONATE_LOGIN: Option = std::env::var("ZED_IMPERSONATE") .ok() .and_then(|s| if s.is_empty() { None } else { Some(s) }); @@ -73,13 +72,45 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [SignIn, SignOut, Reconnect]); +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ClientSettingsContent { + server_url: Option, +} + +#[derive(Deserialize)] +pub struct ClientSettings { + pub server_url: String, +} + +impl Settings for ClientSettings { + const KEY: Option<&'static str> = None; + + type FileContent = ClientSettingsContent; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &mut AppContext, + ) -> Result + where + Self: Sized, + { + let mut result = Self::load_via_json_merge(default_value, user_values)?; + if let Some(server_url) = &*ZED_SERVER_URL { + result.server_url = server_url.clone() + } + Ok(result) + } +} + pub fn init_settings(cx: &mut AppContext) { TelemetrySettings::register(cx); + cx.update_global(|store: &mut SettingsStore, cx| { + store.register_setting::(cx); + }); } pub fn init(client: &Arc, cx: &mut AppContext) { - init_settings(cx); - let client = Arc::downgrade(client); cx.on_action({ let client = client.clone(); @@ -121,7 +152,7 @@ pub fn init(client: &Arc, cx: &mut AppContext) { pub struct Client { id: AtomicU64, peer: Arc, - http: Arc, + http: Arc, telemetry: Arc, state: RwLock, @@ -390,8 +421,8 @@ impl settings::Settings for TelemetrySettings { } impl Client { - pub fn new(http: Arc, cx: &mut AppContext) -> Arc { - Arc::new(Self { + pub fn new(http: Arc, cx: &mut AppContext) -> Arc { + let client = Arc::new(Self { id: AtomicU64::new(0), peer: Peer::new(0), telemetry: Telemetry::new(http.clone(), cx), @@ -402,14 +433,16 @@ impl Client { authenticate: Default::default(), #[cfg(any(test, feature = "test-support"))] establish_connection: Default::default(), - }) + }); + + client } pub fn id(&self) -> u64 { self.id.load(std::sync::atomic::Ordering::SeqCst) } - pub fn http_client(&self) -> Arc { + pub fn http_client(&self) -> Arc { self.http.clone() } @@ -925,14 +958,14 @@ impl Client { } async fn get_rpc_url( - http: Arc, + http: Arc, release_channel: Option, ) -> Result { if let Some(url) = &*ZED_RPC_URL { return Url::parse(url).context("invalid rpc url"); } - let mut url = format!("{}/rpc", *ZED_SERVER_URL); + let mut url = http.zed_url("/rpc"); if let Some(preview_param) = release_channel.and_then(|channel| channel.release_query_param()) { @@ -1053,10 +1086,10 @@ impl Client { // Open the Zed sign-in page in the user's browser, with query parameters that indicate // that the user is signing in from a Zed app running on the same device. - let mut url = format!( - "{}/native_app_signin?native_app_port={}&native_app_public_key={}", - *ZED_SERVER_URL, port, public_key_string - ); + let mut url = http.zed_url(&format!( + "/native_app_signin?native_app_port={}&native_app_public_key={}", + port, public_key_string + )); if let Some(impersonate_login) = IMPERSONATE_LOGIN.as_ref() { log::info!("impersonating user @{}", impersonate_login); @@ -1088,7 +1121,7 @@ impl Client { } let post_auth_url = - format!("{}/native_app_signin_succeeded", *ZED_SERVER_URL); + http.zed_url("/native_app_signin_succeeded"); req.respond( tiny_http::Response::empty(302).with_header( tiny_http::Header::from_bytes( @@ -1130,7 +1163,7 @@ impl Client { } async fn authenticate_as_admin( - http: Arc, + http: Arc, login: String, mut api_token: String, ) -> Result { @@ -1351,7 +1384,7 @@ async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option Result<()> { cx.update(move |cx| { cx.write_credentials( - &ZED_SERVER_URL, + &ClientSettings::get_global(cx).server_url, &credentials.user_id.to_string(), credentials.access_token.as_bytes(), ) @@ -1377,7 +1410,7 @@ async fn write_credentials_to_keychain( } async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> { - cx.update(move |cx| cx.delete_credentials(&ZED_SERVER_URL))? + cx.update(move |cx| cx.delete_credentials(&ClientSettings::get_global(cx).server_url))? .await } @@ -1684,6 +1717,7 @@ mod tests { cx.update(|cx| { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); + init_settings(cx); }); } } diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 824ac3f9ea..2132cee38b 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -1,10 +1,9 @@ mod event_coalescer; -use crate::{TelemetrySettings, ZED_SERVER_URL}; +use crate::TelemetrySettings; use chrono::{DateTime, Utc}; use futures::Future; use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task}; -use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Serialize; use settings::{Settings, SettingsStore}; @@ -13,7 +12,7 @@ use sysinfo::{ CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt, }; use tempfile::NamedTempFile; -use util::http::HttpClient; +use util::http::{HttpClient, ZedHttpClient}; #[cfg(not(debug_assertions))] use util::ResultExt; use util::{channel::ReleaseChannel, TryFutureExt}; @@ -21,7 +20,7 @@ use util::{channel::ReleaseChannel, TryFutureExt}; use self::event_coalescer::EventCoalescer; pub struct Telemetry { - http_client: Arc, + http_client: Arc, executor: BackgroundExecutor, state: Arc>, } @@ -43,12 +42,6 @@ struct TelemetryState { max_queue_size: usize, } -const EVENTS_URL_PATH: &'static str = "/api/events"; - -lazy_static! { - static ref EVENTS_URL: String = format!("{}{}", *ZED_SERVER_URL, EVENTS_URL_PATH); -} - #[derive(Serialize, Debug)] struct EventRequestBody { installation_id: Option>, @@ -149,7 +142,7 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(1); const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5); impl Telemetry { - pub fn new(client: Arc, cx: &mut AppContext) -> Arc { + pub fn new(client: Arc, cx: &mut AppContext) -> Arc { let release_channel = cx .try_global::() .map(|release_channel| release_channel.display_name()); @@ -548,7 +541,7 @@ impl Telemetry { } this.http_client - .post_json(EVENTS_URL.as_str(), json_bytes.into()) + .post_json(&this.http_client.zed_url("/api/events"), json_bytes.into()) .await?; anyhow::Ok(()) } diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 757add782c..e10f03605a 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -153,6 +153,7 @@ impl TestServer { } let settings = SettingsStore::test(cx); cx.set_global(settings); + client::init_settings(cx); }); let http = FakeHttpClient::with_404_response(); diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index 5067551d5c..536059989f 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -2,7 +2,7 @@ use std::{ops::RangeInclusive, sync::Arc, time::Duration}; use anyhow::{anyhow, bail}; use bitflags::bitflags; -use client::{Client, ZED_SERVER_URL}; +use client::Client; use db::kvp::KEY_VALUE_STORE; use editor::{Editor, EditorEvent}; use futures::AsyncReadExt; @@ -16,7 +16,7 @@ use project::Project; use regex::Regex; use serde_derive::Serialize; use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip}; -use util::ResultExt; +use util::{http::HttpClient, ResultExt}; use workspace::{ModalView, Toast, Workspace}; use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedRepo}; @@ -293,12 +293,12 @@ impl FeedbackModal { } } - let feedback_endpoint = format!("{}/api/feedback", *ZED_SERVER_URL); let telemetry = zed_client.telemetry(); let metrics_id = telemetry.metrics_id(); let installation_id = telemetry.installation_id(); let is_staff = telemetry.is_staff(); let http_client = zed_client.http_client(); + let feedback_endpoint = http_client.zed_url("/api/feedback"); let request = FeedbackRequestBody { feedback_text: &feedback_text, email, diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 78bfefd4fa..e8c0297def 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Context, Result}; use collections::{btree_map, hash_map, BTreeMap, HashMap}; -use gpui::AppContext; +use gpui::{AppContext, AsyncAppContext}; use lazy_static::lazy_static; use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema}; use serde::{de::DeserializeOwned, Deserialize as _, Serialize}; @@ -13,7 +13,9 @@ use std::{ str, sync::Arc, }; -use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _}; +use util::{ + channel::RELEASE_CHANNEL_NAME, merge_non_null_json_value_into, RangeExt, ResultExt as _, +}; /// A value that can be defined as a user setting. /// @@ -102,6 +104,14 @@ pub trait Settings: 'static + Send + Sync { cx.global::().get(None) } + #[track_caller] + fn try_read_global<'a, R>(cx: &'a AsyncAppContext, f: impl FnOnce(&Self) -> R) -> Option + where + Self: Sized, + { + cx.try_read_global(|s: &SettingsStore, _| f(s.get(None))) + } + #[track_caller] fn override_global<'a>(settings: Self, cx: &'a mut AppContext) where @@ -197,6 +207,15 @@ impl SettingsStore { user_values_stack = vec![user_settings]; } + if let Some(release_settings) = &self.raw_user_settings.get(&*RELEASE_CHANNEL_NAME) { + if let Some(release_settings) = setting_value + .deserialize_setting(&release_settings) + .log_err() + { + user_values_stack.push(release_settings); + } + } + if let Some(setting) = setting_value .load_setting(&default_settings, &user_values_stack, cx) .context("A default setting must be added to the `default.json` file") @@ -484,6 +503,15 @@ impl SettingsStore { } } + for release_stage in ["dev", "nightly", "stable", "preview"] { + let schema = combined_schema.schema.clone(); + combined_schema + .schema + .object() + .properties + .insert(release_stage.to_string(), schema.into()); + } + serde_json::to_value(&combined_schema).unwrap() } @@ -509,6 +537,16 @@ impl SettingsStore { paths_stack.push(None); } + if let Some(release_settings) = &self.raw_user_settings.get(&*RELEASE_CHANNEL_NAME) { + if let Some(release_settings) = setting_value + .deserialize_setting(&release_settings) + .log_err() + { + user_settings_stack.push(release_settings); + paths_stack.push(None); + } + } + // If the global settings file changed, reload the global value for the field. if changed_local_path.is_none() { if let Some(value) = setting_value diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 211cec7d25..924c6fe688 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true git2 = { workspace = true, optional = true } dirs = "3.0" take-until = "0.2.0" +parking_lot.workspace = true [target.'cfg(windows)'.dependencies] tendril = "0.4.3" diff --git a/crates/util/src/http.rs b/crates/util/src/http.rs index f6487c04f2..6218c59c2b 100644 --- a/crates/util/src/http.rs +++ b/crates/util/src/http.rs @@ -6,12 +6,49 @@ pub use isahc::{ Error, }; pub use isahc::{AsyncBody, Request, Response}; +use parking_lot::Mutex; use smol::future::FutureExt; #[cfg(feature = "test-support")] use std::fmt; use std::{sync::Arc, time::Duration}; pub use url::Url; +pub struct ZedHttpClient { + pub zed_host: Mutex, + client: Box, +} + +impl ZedHttpClient { + pub fn zed_url(&self, path: &str) -> String { + format!("{}{}", self.zed_host.lock(), path) + } +} + +impl HttpClient for Arc { + fn send(&self, req: Request) -> BoxFuture, Error>> { + self.client.send(req) + } +} + +impl HttpClient for ZedHttpClient { + fn send(&self, req: Request) -> BoxFuture, Error>> { + self.client.send(req) + } +} + +pub fn zed_client(zed_host: &str) -> Arc { + Arc::new(ZedHttpClient { + zed_host: Mutex::new(zed_host.to_string()), + client: Box::new( + isahc::HttpClient::builder() + .connect_timeout(Duration::from_secs(5)) + .low_speed_timeout(100, Duration::from_secs(5)) + .build() + .unwrap(), + ), + }) +} + pub trait HttpClient: Send + Sync { fn send(&self, req: Request) -> BoxFuture, Error>>; @@ -81,17 +118,20 @@ pub struct FakeHttpClient { #[cfg(feature = "test-support")] impl FakeHttpClient { - pub fn create(handler: F) -> Arc + pub fn create(handler: F) -> Arc where Fut: 'static + Send + futures::Future, Error>>, F: 'static + Send + Sync + Fn(Request) -> Fut, { - Arc::new(Self { - handler: Box::new(move |req| Box::pin(handler(req))), + Arc::new(ZedHttpClient { + zed_host: Mutex::new("http://test.example".into()), + client: Box::new(Self { + handler: Box::new(move |req| Box::pin(handler(req))), + }), }) } - pub fn with_404_response() -> Arc { + pub fn with_404_response() -> Arc { Self::create(|_| async move { Ok(Response::builder() .status(404) @@ -100,7 +140,7 @@ impl FakeHttpClient { }) } - pub fn with_200_response() -> Arc { + pub fn with_200_response() -> Arc { Self::create(|_| async move { Ok(Response::builder() .status(200) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 5955d79a59..d045637f63 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -42,7 +42,7 @@ use theme::{ActiveTheme, ThemeRegistry, ThemeSettings}; use util::{ async_maybe, channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL}, - http::{self, HttpClient}, + http::{self, HttpClient, ZedHttpClient}, paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR}, ResultExt, }; @@ -59,7 +59,6 @@ fn main() { menu::init(); zed_actions::init(); - let http = http::client(); init_paths(); init_logger(); @@ -132,6 +131,9 @@ fn main() { cx.set_global(store); handle_settings_file_changes(user_settings_file_rx, cx); handle_keymap_file_changes(user_keymap_file_rx, cx); + client::init_settings(cx); + + let http = http::zed_client(&client::ClientSettings::get_global(cx).server_url); let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); @@ -201,7 +203,20 @@ fn main() { languages.set_theme(cx.theme().clone()); cx.observe_global::({ let languages = languages.clone(); - move |cx| languages.set_theme(cx.theme().clone()) + let http = http.clone(); + let client = client.clone(); + + move |cx| { + languages.set_theme(cx.theme().clone()); + let new_host = &client::ClientSettings::get_global(cx).server_url; + let mut host = http.zed_host.lock(); + if &*host != new_host { + *host = new_host.clone(); + if client.status().borrow().is_connected() { + client.reconnect(&cx.to_async()); + } + } + } }) .detach(); @@ -230,7 +245,7 @@ fn main() { cx.set_global(Arc::downgrade(&app_state)); audio::init(Assets, cx); - auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx); + auto_update::init(http.clone(), cx); workspace::init(app_state.clone(), cx); recent_projects::init(cx); @@ -634,7 +649,7 @@ fn init_panic_hook(app: &App, installation_id: Option, session_id: Strin })); } -fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { +fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { let telemetry_settings = *client::TelemetrySettings::get_global(cx); cx.background_executor() .spawn(async move { @@ -650,10 +665,10 @@ fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { /// upload panics to us (via zed.dev) async fn upload_previous_panics( - http: Arc, + http: Arc, telemetry_settings: client::TelemetrySettings, ) -> Result<()> { - let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL); + let panic_report_url = http.zed_url("/api/panic"); let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; while let Some(child) = children.next().await { let child = child?; @@ -717,7 +732,7 @@ static LAST_CRASH_UPLOADED: &'static str = "LAST_CRASH_UPLOADED"; /// upload crashes from apple's diagnostic reports to our server. /// (only if telemetry is enabled) async fn upload_previous_crashes( - http: Arc, + http: Arc, telemetry_settings: client::TelemetrySettings, ) -> Result<()> { if !telemetry_settings.diagnostics { @@ -728,7 +743,7 @@ async fn upload_previous_crashes( .unwrap_or("zed-2024-01-17-221900.ips".to_string()); // don't upload old crash reports from before we had this. let mut uploaded = last_uploaded.clone(); - let crash_report_url = format!("{}/api/crash", &*client::ZED_SERVER_URL); + let crash_report_url = http.zed_url("/api/crash"); for dir in [&*CRASHES_DIR, &*CRASHES_RETIRED_DIR] { let mut children = smol::fs::read_dir(&dir).await?;