nightly url setting (#7037)
Release Notes: - Added the ability to set settings per-release stage - Added a `"server_url"` setting
This commit is contained in:
parent
c07355265f
commit
dfbcaf36fc
12 changed files with 217 additions and 76 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -9025,6 +9025,7 @@ dependencies = [
|
||||||
"isahc",
|
"isahc",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,11 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use update_notification::UpdateNotification;
|
use update_notification::UpdateNotification;
|
||||||
use util::channel::{AppCommitSha, ReleaseChannel};
|
|
||||||
use util::http::HttpClient;
|
use util::http::HttpClient;
|
||||||
|
use util::{
|
||||||
|
channel::{AppCommitSha, ReleaseChannel},
|
||||||
|
http::ZedHttpClient,
|
||||||
|
};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
|
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
|
||||||
|
@ -54,9 +57,8 @@ pub enum AutoUpdateStatus {
|
||||||
pub struct AutoUpdater {
|
pub struct AutoUpdater {
|
||||||
status: AutoUpdateStatus,
|
status: AutoUpdateStatus,
|
||||||
current_version: SemanticVersion,
|
current_version: SemanticVersion,
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<ZedHttpClient>,
|
||||||
pending_poll: Option<Task<Option<()>>>,
|
pending_poll: Option<Task<Option<()>>>,
|
||||||
server_url: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -92,7 +94,7 @@ impl Settings for AutoUpdateSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppContext) {
|
pub fn init(http_client: Arc<ZedHttpClient>, cx: &mut AppContext) {
|
||||||
AutoUpdateSetting::register(cx);
|
AutoUpdateSetting::register(cx);
|
||||||
|
|
||||||
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
|
cx.observe_new_views(|workspace: &mut Workspace, _cx| {
|
||||||
|
@ -106,7 +108,7 @@ pub fn init(http_client: Arc<dyn HttpClient>, server_url: String, cx: &mut AppCo
|
||||||
|
|
||||||
if let Some(version) = ZED_APP_VERSION.or_else(|| cx.app_metadata().app_version) {
|
if let Some(version) = ZED_APP_VERSION.or_else(|| cx.app_metadata().app_version) {
|
||||||
let auto_updater = cx.new_model(|cx| {
|
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)
|
let mut update_subscription = AutoUpdateSetting::get_global(cx)
|
||||||
.0
|
.0
|
||||||
|
@ -151,10 +153,11 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<(
|
||||||
ReleaseChannel::Stable | ReleaseChannel::Preview
|
ReleaseChannel::Stable | ReleaseChannel::Preview
|
||||||
) {
|
) {
|
||||||
let auto_updater = auto_updater.read(cx);
|
let auto_updater = auto_updater.read(cx);
|
||||||
let server_url = &auto_updater.server_url;
|
|
||||||
let release_channel = release_channel.dev_name();
|
let release_channel = release_channel.dev_name();
|
||||||
let current_version = auto_updater.current_version;
|
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);
|
cx.open_url(&url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,16 +194,11 @@ impl AutoUpdater {
|
||||||
cx.default_global::<Option<Model<Self>>>().clone()
|
cx.default_global::<Option<Model<Self>>>().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(
|
fn new(current_version: SemanticVersion, http_client: Arc<ZedHttpClient>) -> Self {
|
||||||
current_version: SemanticVersion,
|
|
||||||
http_client: Arc<dyn HttpClient>,
|
|
||||||
server_url: String,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
status: AutoUpdateStatus::Idle,
|
status: AutoUpdateStatus::Idle,
|
||||||
current_version,
|
current_version,
|
||||||
http_client,
|
http_client,
|
||||||
server_url,
|
|
||||||
pending_poll: None,
|
pending_poll: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,18 +244,14 @@ impl AutoUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
|
async fn update(this: Model<Self>, mut cx: AsyncAppContext) -> Result<()> {
|
||||||
let (client, server_url, current_version) = this.read_with(&cx, |this, _| {
|
let (client, current_version) = this.read_with(&cx, |this, _| {
|
||||||
(
|
(this.http_client.clone(), this.current_version)
|
||||||
this.http_client.clone(),
|
|
||||||
this.server_url.clone(),
|
|
||||||
this.current_version,
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut url_string = format!(
|
let mut url_string = client.zed_url(&format!(
|
||||||
"{server_url}/api/releases/latest?asset=Zed.dmg&os={}&arch={}",
|
"/api/releases/latest?asset=Zed.dmg&os={}&arch={}",
|
||||||
OS, ARCH
|
OS, ARCH
|
||||||
);
|
));
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
if let Some(param) = cx
|
if let Some(param) = cx
|
||||||
.try_global::<ReleaseChannel>()
|
.try_global::<ReleaseChannel>()
|
||||||
|
|
|
@ -329,6 +329,7 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
||||||
fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
|
fn init_test(cx: &mut AppContext) -> Model<ChannelStore> {
|
||||||
let settings_store = SettingsStore::test(cx);
|
let settings_store = SettingsStore::test(cx);
|
||||||
cx.set_global(settings_store);
|
cx.set_global(settings_store);
|
||||||
|
client::init_settings(cx);
|
||||||
|
|
||||||
let http = FakeHttpClient::with_404_response();
|
let http = FakeHttpClient::with_404_response();
|
||||||
let client = Client::new(http.clone(), cx);
|
let client = Client::new(http.clone(), cx);
|
||||||
|
|
|
@ -26,7 +26,7 @@ use rpc::proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, PeerId, Requ
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use settings::Settings;
|
use settings::{Settings, SettingsStore};
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -41,8 +41,8 @@ use std::{
|
||||||
use telemetry::Telemetry;
|
use telemetry::Telemetry;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::channel::ReleaseChannel;
|
|
||||||
use util::http::HttpClient;
|
use util::http::HttpClient;
|
||||||
|
use util::{channel::ReleaseChannel, http::ZedHttpClient};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
|
||||||
pub use rpc::*;
|
pub use rpc::*;
|
||||||
|
@ -50,9 +50,8 @@ pub use telemetry::Event;
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref ZED_SERVER_URL: String =
|
static ref ZED_SERVER_URL: Option<String> = std::env::var("ZED_SERVER_URL").ok();
|
||||||
std::env::var("ZED_SERVER_URL").unwrap_or_else(|_| "https://zed.dev".to_string());
|
static ref ZED_RPC_URL: Option<String> = std::env::var("ZED_RPC_URL").ok();
|
||||||
pub static ref ZED_RPC_URL: Option<String> = std::env::var("ZED_RPC_URL").ok();
|
|
||||||
pub static ref IMPERSONATE_LOGIN: Option<String> = std::env::var("ZED_IMPERSONATE")
|
pub static ref IMPERSONATE_LOGIN: Option<String> = std::env::var("ZED_IMPERSONATE")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| if s.is_empty() { None } else { Some(s) });
|
.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]);
|
actions!(client, [SignIn, SignOut, Reconnect]);
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct ClientSettingsContent {
|
||||||
|
server_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Self>
|
||||||
|
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) {
|
pub fn init_settings(cx: &mut AppContext) {
|
||||||
TelemetrySettings::register(cx);
|
TelemetrySettings::register(cx);
|
||||||
|
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||||
|
store.register_setting::<ClientSettings>(cx);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
|
pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
|
||||||
init_settings(cx);
|
|
||||||
|
|
||||||
let client = Arc::downgrade(client);
|
let client = Arc::downgrade(client);
|
||||||
cx.on_action({
|
cx.on_action({
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
|
@ -121,7 +152,7 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
id: AtomicU64,
|
id: AtomicU64,
|
||||||
peer: Arc<Peer>,
|
peer: Arc<Peer>,
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<ZedHttpClient>,
|
||||||
telemetry: Arc<Telemetry>,
|
telemetry: Arc<Telemetry>,
|
||||||
state: RwLock<ClientState>,
|
state: RwLock<ClientState>,
|
||||||
|
|
||||||
|
@ -390,8 +421,8 @@ impl settings::Settings for TelemetrySettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn new(http: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
|
pub fn new(http: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
let client = Arc::new(Self {
|
||||||
id: AtomicU64::new(0),
|
id: AtomicU64::new(0),
|
||||||
peer: Peer::new(0),
|
peer: Peer::new(0),
|
||||||
telemetry: Telemetry::new(http.clone(), cx),
|
telemetry: Telemetry::new(http.clone(), cx),
|
||||||
|
@ -402,14 +433,16 @@ impl Client {
|
||||||
authenticate: Default::default(),
|
authenticate: Default::default(),
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
establish_connection: Default::default(),
|
establish_connection: Default::default(),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> u64 {
|
pub fn id(&self) -> u64 {
|
||||||
self.id.load(std::sync::atomic::Ordering::SeqCst)
|
self.id.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn http_client(&self) -> Arc<dyn HttpClient> {
|
pub fn http_client(&self) -> Arc<ZedHttpClient> {
|
||||||
self.http.clone()
|
self.http.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,14 +958,14 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_rpc_url(
|
async fn get_rpc_url(
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<ZedHttpClient>,
|
||||||
release_channel: Option<ReleaseChannel>,
|
release_channel: Option<ReleaseChannel>,
|
||||||
) -> Result<Url> {
|
) -> Result<Url> {
|
||||||
if let Some(url) = &*ZED_RPC_URL {
|
if let Some(url) = &*ZED_RPC_URL {
|
||||||
return Url::parse(url).context("invalid 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) =
|
if let Some(preview_param) =
|
||||||
release_channel.and_then(|channel| channel.release_query_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
|
// 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.
|
// that the user is signing in from a Zed app running on the same device.
|
||||||
let mut url = format!(
|
let mut url = http.zed_url(&format!(
|
||||||
"{}/native_app_signin?native_app_port={}&native_app_public_key={}",
|
"/native_app_signin?native_app_port={}&native_app_public_key={}",
|
||||||
*ZED_SERVER_URL, port, public_key_string
|
port, public_key_string
|
||||||
);
|
));
|
||||||
|
|
||||||
if let Some(impersonate_login) = IMPERSONATE_LOGIN.as_ref() {
|
if let Some(impersonate_login) = IMPERSONATE_LOGIN.as_ref() {
|
||||||
log::info!("impersonating user @{}", impersonate_login);
|
log::info!("impersonating user @{}", impersonate_login);
|
||||||
|
@ -1088,7 +1121,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
let post_auth_url =
|
let post_auth_url =
|
||||||
format!("{}/native_app_signin_succeeded", *ZED_SERVER_URL);
|
http.zed_url("/native_app_signin_succeeded");
|
||||||
req.respond(
|
req.respond(
|
||||||
tiny_http::Response::empty(302).with_header(
|
tiny_http::Response::empty(302).with_header(
|
||||||
tiny_http::Header::from_bytes(
|
tiny_http::Header::from_bytes(
|
||||||
|
@ -1130,7 +1163,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn authenticate_as_admin(
|
async fn authenticate_as_admin(
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<ZedHttpClient>,
|
||||||
login: String,
|
login: String,
|
||||||
mut api_token: String,
|
mut api_token: String,
|
||||||
) -> Result<Credentials> {
|
) -> Result<Credentials> {
|
||||||
|
@ -1351,7 +1384,7 @@ async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credenti
|
||||||
}
|
}
|
||||||
|
|
||||||
let (user_id, access_token) = cx
|
let (user_id, access_token) = cx
|
||||||
.update(|cx| cx.read_credentials(&ZED_SERVER_URL))
|
.update(|cx| cx.read_credentials(&ClientSettings::get_global(cx).server_url))
|
||||||
.log_err()?
|
.log_err()?
|
||||||
.await
|
.await
|
||||||
.log_err()??;
|
.log_err()??;
|
||||||
|
@ -1368,7 +1401,7 @@ async fn write_credentials_to_keychain(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
cx.update(move |cx| {
|
cx.update(move |cx| {
|
||||||
cx.write_credentials(
|
cx.write_credentials(
|
||||||
&ZED_SERVER_URL,
|
&ClientSettings::get_global(cx).server_url,
|
||||||
&credentials.user_id.to_string(),
|
&credentials.user_id.to_string(),
|
||||||
credentials.access_token.as_bytes(),
|
credentials.access_token.as_bytes(),
|
||||||
)
|
)
|
||||||
|
@ -1377,7 +1410,7 @@ async fn write_credentials_to_keychain(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1684,6 +1717,7 @@ mod tests {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let settings_store = SettingsStore::test(cx);
|
let settings_store = SettingsStore::test(cx);
|
||||||
cx.set_global(settings_store);
|
cx.set_global(settings_store);
|
||||||
|
init_settings(cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
mod event_coalescer;
|
mod event_coalescer;
|
||||||
|
|
||||||
use crate::{TelemetrySettings, ZED_SERVER_URL};
|
use crate::TelemetrySettings;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
|
use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
|
@ -13,7 +12,7 @@ use sysinfo::{
|
||||||
CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt,
|
CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt,
|
||||||
};
|
};
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use util::http::HttpClient;
|
use util::http::{HttpClient, ZedHttpClient};
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use util::{channel::ReleaseChannel, TryFutureExt};
|
use util::{channel::ReleaseChannel, TryFutureExt};
|
||||||
|
@ -21,7 +20,7 @@ use util::{channel::ReleaseChannel, TryFutureExt};
|
||||||
use self::event_coalescer::EventCoalescer;
|
use self::event_coalescer::EventCoalescer;
|
||||||
|
|
||||||
pub struct Telemetry {
|
pub struct Telemetry {
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<ZedHttpClient>,
|
||||||
executor: BackgroundExecutor,
|
executor: BackgroundExecutor,
|
||||||
state: Arc<Mutex<TelemetryState>>,
|
state: Arc<Mutex<TelemetryState>>,
|
||||||
}
|
}
|
||||||
|
@ -43,12 +42,6 @@ struct TelemetryState {
|
||||||
max_queue_size: usize,
|
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)]
|
#[derive(Serialize, Debug)]
|
||||||
struct EventRequestBody {
|
struct EventRequestBody {
|
||||||
installation_id: Option<Arc<str>>,
|
installation_id: Option<Arc<str>>,
|
||||||
|
@ -149,7 +142,7 @@ const FLUSH_INTERVAL: Duration = Duration::from_secs(1);
|
||||||
const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
|
const FLUSH_INTERVAL: Duration = Duration::from_secs(60 * 5);
|
||||||
|
|
||||||
impl Telemetry {
|
impl Telemetry {
|
||||||
pub fn new(client: Arc<dyn HttpClient>, cx: &mut AppContext) -> Arc<Self> {
|
pub fn new(client: Arc<ZedHttpClient>, cx: &mut AppContext) -> Arc<Self> {
|
||||||
let release_channel = cx
|
let release_channel = cx
|
||||||
.try_global::<ReleaseChannel>()
|
.try_global::<ReleaseChannel>()
|
||||||
.map(|release_channel| release_channel.display_name());
|
.map(|release_channel| release_channel.display_name());
|
||||||
|
@ -548,7 +541,7 @@ impl Telemetry {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http_client
|
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?;
|
.await?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ impl TestServer {
|
||||||
}
|
}
|
||||||
let settings = SettingsStore::test(cx);
|
let settings = SettingsStore::test(cx);
|
||||||
cx.set_global(settings);
|
cx.set_global(settings);
|
||||||
|
client::init_settings(cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let http = FakeHttpClient::with_404_response();
|
let http = FakeHttpClient::with_404_response();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{ops::RangeInclusive, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use client::{Client, ZED_SERVER_URL};
|
use client::Client;
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{Editor, EditorEvent};
|
use editor::{Editor, EditorEvent};
|
||||||
use futures::AsyncReadExt;
|
use futures::AsyncReadExt;
|
||||||
|
@ -16,7 +16,7 @@ use project::Project;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
|
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
|
||||||
use util::ResultExt;
|
use util::{http::HttpClient, ResultExt};
|
||||||
use workspace::{ModalView, Toast, Workspace};
|
use workspace::{ModalView, Toast, Workspace};
|
||||||
|
|
||||||
use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedRepo};
|
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 telemetry = zed_client.telemetry();
|
||||||
let metrics_id = telemetry.metrics_id();
|
let metrics_id = telemetry.metrics_id();
|
||||||
let installation_id = telemetry.installation_id();
|
let installation_id = telemetry.installation_id();
|
||||||
let is_staff = telemetry.is_staff();
|
let is_staff = telemetry.is_staff();
|
||||||
let http_client = zed_client.http_client();
|
let http_client = zed_client.http_client();
|
||||||
|
let feedback_endpoint = http_client.zed_url("/api/feedback");
|
||||||
let request = FeedbackRequestBody {
|
let request = FeedbackRequestBody {
|
||||||
feedback_text: &feedback_text,
|
feedback_text: &feedback_text,
|
||||||
email,
|
email,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use collections::{btree_map, hash_map, BTreeMap, HashMap};
|
use collections::{btree_map, hash_map, BTreeMap, HashMap};
|
||||||
use gpui::AppContext;
|
use gpui::{AppContext, AsyncAppContext};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
|
use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
|
||||||
use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
|
||||||
|
@ -13,7 +13,9 @@ use std::{
|
||||||
str,
|
str,
|
||||||
sync::Arc,
|
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.
|
/// A value that can be defined as a user setting.
|
||||||
///
|
///
|
||||||
|
@ -102,6 +104,14 @@ pub trait Settings: 'static + Send + Sync {
|
||||||
cx.global::<SettingsStore>().get(None)
|
cx.global::<SettingsStore>().get(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn try_read_global<'a, R>(cx: &'a AsyncAppContext, f: impl FnOnce(&Self) -> R) -> Option<R>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
cx.try_read_global(|s: &SettingsStore, _| f(s.get(None)))
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn override_global<'a>(settings: Self, cx: &'a mut AppContext)
|
fn override_global<'a>(settings: Self, cx: &'a mut AppContext)
|
||||||
where
|
where
|
||||||
|
@ -197,6 +207,15 @@ impl SettingsStore {
|
||||||
user_values_stack = vec![user_settings];
|
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
|
if let Some(setting) = setting_value
|
||||||
.load_setting(&default_settings, &user_values_stack, cx)
|
.load_setting(&default_settings, &user_values_stack, cx)
|
||||||
.context("A default setting must be added to the `default.json` file")
|
.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()
|
serde_json::to_value(&combined_schema).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +537,16 @@ impl SettingsStore {
|
||||||
paths_stack.push(None);
|
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 the global settings file changed, reload the global value for the field.
|
||||||
if changed_local_path.is_none() {
|
if changed_local_path.is_none() {
|
||||||
if let Some(value) = setting_value
|
if let Some(value) = setting_value
|
||||||
|
|
|
@ -30,6 +30,7 @@ serde_json.workspace = true
|
||||||
git2 = { workspace = true, optional = true }
|
git2 = { workspace = true, optional = true }
|
||||||
dirs = "3.0"
|
dirs = "3.0"
|
||||||
take-until = "0.2.0"
|
take-until = "0.2.0"
|
||||||
|
parking_lot.workspace = true
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
tendril = "0.4.3"
|
tendril = "0.4.3"
|
||||||
|
|
|
@ -6,12 +6,49 @@ pub use isahc::{
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
pub use isahc::{AsyncBody, Request, Response};
|
pub use isahc::{AsyncBody, Request, Response};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use smol::future::FutureExt;
|
use smol::future::FutureExt;
|
||||||
#[cfg(feature = "test-support")]
|
#[cfg(feature = "test-support")]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
pub use url::Url;
|
pub use url::Url;
|
||||||
|
|
||||||
|
pub struct ZedHttpClient {
|
||||||
|
pub zed_host: Mutex<String>,
|
||||||
|
client: Box<dyn HttpClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZedHttpClient {
|
||||||
|
pub fn zed_url(&self, path: &str) -> String {
|
||||||
|
format!("{}{}", self.zed_host.lock(), path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpClient for Arc<ZedHttpClient> {
|
||||||
|
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>> {
|
||||||
|
self.client.send(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpClient for ZedHttpClient {
|
||||||
|
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>> {
|
||||||
|
self.client.send(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zed_client(zed_host: &str) -> Arc<ZedHttpClient> {
|
||||||
|
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 {
|
pub trait HttpClient: Send + Sync {
|
||||||
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>>;
|
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>>;
|
||||||
|
|
||||||
|
@ -81,17 +118,20 @@ pub struct FakeHttpClient {
|
||||||
|
|
||||||
#[cfg(feature = "test-support")]
|
#[cfg(feature = "test-support")]
|
||||||
impl FakeHttpClient {
|
impl FakeHttpClient {
|
||||||
pub fn create<Fut, F>(handler: F) -> Arc<dyn HttpClient>
|
pub fn create<Fut, F>(handler: F) -> Arc<ZedHttpClient>
|
||||||
where
|
where
|
||||||
Fut: 'static + Send + futures::Future<Output = Result<Response<AsyncBody>, Error>>,
|
Fut: 'static + Send + futures::Future<Output = Result<Response<AsyncBody>, Error>>,
|
||||||
F: 'static + Send + Sync + Fn(Request<AsyncBody>) -> Fut,
|
F: 'static + Send + Sync + Fn(Request<AsyncBody>) -> Fut,
|
||||||
{
|
{
|
||||||
Arc::new(Self {
|
Arc::new(ZedHttpClient {
|
||||||
handler: Box::new(move |req| Box::pin(handler(req))),
|
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<dyn HttpClient> {
|
pub fn with_404_response() -> Arc<ZedHttpClient> {
|
||||||
Self::create(|_| async move {
|
Self::create(|_| async move {
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(404)
|
.status(404)
|
||||||
|
@ -100,7 +140,7 @@ impl FakeHttpClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_200_response() -> Arc<dyn HttpClient> {
|
pub fn with_200_response() -> Arc<ZedHttpClient> {
|
||||||
Self::create(|_| async move {
|
Self::create(|_| async move {
|
||||||
Ok(Response::builder()
|
Ok(Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
|
|
|
@ -42,7 +42,7 @@ use theme::{ActiveTheme, ThemeRegistry, ThemeSettings};
|
||||||
use util::{
|
use util::{
|
||||||
async_maybe,
|
async_maybe,
|
||||||
channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL},
|
channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL},
|
||||||
http::{self, HttpClient},
|
http::{self, HttpClient, ZedHttpClient},
|
||||||
paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR},
|
paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR},
|
||||||
ResultExt,
|
ResultExt,
|
||||||
};
|
};
|
||||||
|
@ -59,7 +59,6 @@ fn main() {
|
||||||
menu::init();
|
menu::init();
|
||||||
zed_actions::init();
|
zed_actions::init();
|
||||||
|
|
||||||
let http = http::client();
|
|
||||||
init_paths();
|
init_paths();
|
||||||
init_logger();
|
init_logger();
|
||||||
|
|
||||||
|
@ -132,6 +131,9 @@ fn main() {
|
||||||
cx.set_global(store);
|
cx.set_global(store);
|
||||||
handle_settings_file_changes(user_settings_file_rx, cx);
|
handle_settings_file_changes(user_settings_file_rx, cx);
|
||||||
handle_keymap_file_changes(user_keymap_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 client = client::Client::new(http.clone(), cx);
|
||||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||||
|
@ -201,7 +203,20 @@ fn main() {
|
||||||
languages.set_theme(cx.theme().clone());
|
languages.set_theme(cx.theme().clone());
|
||||||
cx.observe_global::<SettingsStore>({
|
cx.observe_global::<SettingsStore>({
|
||||||
let languages = languages.clone();
|
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();
|
.detach();
|
||||||
|
|
||||||
|
@ -230,7 +245,7 @@ fn main() {
|
||||||
cx.set_global(Arc::downgrade(&app_state));
|
cx.set_global(Arc::downgrade(&app_state));
|
||||||
|
|
||||||
audio::init(Assets, cx);
|
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);
|
workspace::init(app_state.clone(), cx);
|
||||||
recent_projects::init(cx);
|
recent_projects::init(cx);
|
||||||
|
@ -634,7 +649,7 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_panics_and_crashes(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
fn upload_panics_and_crashes(http: Arc<ZedHttpClient>, cx: &mut AppContext) {
|
||||||
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
|
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
|
||||||
cx.background_executor()
|
cx.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
|
@ -650,10 +665,10 @@ fn upload_panics_and_crashes(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
||||||
|
|
||||||
/// upload panics to us (via zed.dev)
|
/// upload panics to us (via zed.dev)
|
||||||
async fn upload_previous_panics(
|
async fn upload_previous_panics(
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<ZedHttpClient>,
|
||||||
telemetry_settings: client::TelemetrySettings,
|
telemetry_settings: client::TelemetrySettings,
|
||||||
) -> Result<()> {
|
) -> 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?;
|
let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
|
||||||
while let Some(child) = children.next().await {
|
while let Some(child) = children.next().await {
|
||||||
let child = child?;
|
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.
|
/// upload crashes from apple's diagnostic reports to our server.
|
||||||
/// (only if telemetry is enabled)
|
/// (only if telemetry is enabled)
|
||||||
async fn upload_previous_crashes(
|
async fn upload_previous_crashes(
|
||||||
http: Arc<dyn HttpClient>,
|
http: Arc<ZedHttpClient>,
|
||||||
telemetry_settings: client::TelemetrySettings,
|
telemetry_settings: client::TelemetrySettings,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !telemetry_settings.diagnostics {
|
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.
|
.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 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] {
|
for dir in [&*CRASHES_DIR, &*CRASHES_RETIRED_DIR] {
|
||||||
let mut children = smol::fs::read_dir(&dir).await?;
|
let mut children = smol::fs::read_dir(&dir).await?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue