Add telemetry events for loading extensions (#9793)

* Store extensions versions' wasm API version in the database
* Share a common struct for extension API responses between collab and
client
* Add wasm API version and schema version to extension API responses

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-03-25 14:30:48 -07:00 committed by GitHub
parent 9b62e461ed
commit 5adc51f113
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 531 additions and 306 deletions

View file

@ -10,6 +10,7 @@ use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
use anyhow::{anyhow, bail, Context as _, Result};
use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use client::{telemetry::Telemetry, Client};
use collections::{hash_map, BTreeMap, HashMap, HashSet};
use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
use fs::{Fs, RemoveOptions};
@ -30,7 +31,6 @@ use node_runtime::NodeRuntime;
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
ffi::OsStr,
path::{self, Path, PathBuf},
sync::Arc,
time::{Duration, Instant},
@ -75,6 +75,7 @@ pub struct ExtensionStore {
extension_index: ExtensionIndex,
fs: Arc<dyn Fs>,
http_client: Arc<HttpClientWithUrl>,
telemetry: Option<Arc<Telemetry>>,
reload_tx: UnboundedSender<Option<Arc<str>>>,
reload_complete_senders: Vec<oneshot::Sender<()>>,
installed_dir: PathBuf,
@ -149,7 +150,7 @@ actions!(zed, [ReloadExtensions]);
pub fn init(
fs: Arc<fs::RealFs>,
http_client: Arc<HttpClientWithUrl>,
client: Arc<Client>,
node_runtime: Arc<dyn NodeRuntime>,
language_registry: Arc<LanguageRegistry>,
theme_registry: Arc<ThemeRegistry>,
@ -160,7 +161,8 @@ pub fn init(
EXTENSIONS_DIR.clone(),
None,
fs,
http_client,
client.http_client().clone(),
Some(client.telemetry().clone()),
node_runtime,
language_registry,
theme_registry,
@ -187,6 +189,7 @@ impl ExtensionStore {
build_dir: Option<PathBuf>,
fs: Arc<dyn Fs>,
http_client: Arc<HttpClientWithUrl>,
telemetry: Option<Arc<Telemetry>>,
node_runtime: Arc<dyn NodeRuntime>,
language_registry: Arc<LanguageRegistry>,
theme_registry: Arc<ThemeRegistry>,
@ -216,6 +219,7 @@ impl ExtensionStore {
wasm_extensions: Vec::new(),
fs,
http_client,
telemetry,
language_registry,
theme_registry,
reload_tx,
@ -587,8 +591,8 @@ impl ExtensionStore {
let builder = self.builder.clone();
cx.spawn(move |this, mut cx| async move {
let extension_manifest =
Self::load_extension_manifest(fs.clone(), &extension_source_path).await?;
let mut extension_manifest =
ExtensionManifest::load(fs.clone(), &extension_source_path).await?;
let extension_id = extension_manifest.id.clone();
if !this.update(&mut cx, |this, cx| {
@ -622,7 +626,7 @@ impl ExtensionStore {
builder
.compile_extension(
&extension_source_path,
&extension_manifest,
&mut extension_manifest,
CompileExtensionOptions { release: false },
)
.await
@ -667,9 +671,13 @@ impl ExtensionStore {
cx.notify();
let compile = cx.background_executor().spawn(async move {
let manifest = Self::load_extension_manifest(fs, &path).await?;
let mut manifest = ExtensionManifest::load(fs, &path).await?;
builder
.compile_extension(&path, &manifest, CompileExtensionOptions { release: true })
.compile_extension(
&path,
&mut manifest,
CompileExtensionOptions { release: true },
)
.await
});
@ -759,6 +767,17 @@ impl ExtensionStore {
extensions_to_unload.len() - reload_count
);
if let Some(telemetry) = &self.telemetry {
for extension_id in &extensions_to_load {
if let Some(extension) = self.extension_index.extensions.get(extension_id) {
telemetry.report_extension_event(
extension_id.clone(),
extension.manifest.version.clone(),
);
}
}
}
let themes_to_remove = old_index
.themes
.iter()
@ -908,7 +927,9 @@ impl ExtensionStore {
cx.background_executor().clone(),
)
.await
.context("failed to load wasm extension")
.with_context(|| {
format!("failed to load wasm extension {}", extension.manifest.id)
})
})
.await;
@ -989,8 +1010,7 @@ impl ExtensionStore {
extension_dir: PathBuf,
index: &mut ExtensionIndex,
) -> Result<()> {
let mut extension_manifest =
Self::load_extension_manifest(fs.clone(), &extension_dir).await?;
let mut extension_manifest = ExtensionManifest::load(fs.clone(), &extension_dir).await?;
let extension_id = extension_manifest.id.clone();
// TODO: distinguish dev extensions more explicitly, by the absence
@ -1082,72 +1102,6 @@ impl ExtensionStore {
Ok(())
}
pub async fn load_extension_manifest(
fs: Arc<dyn Fs>,
extension_dir: &Path,
) -> Result<ExtensionManifest> {
let extension_name = extension_dir
.file_name()
.and_then(OsStr::to_str)
.ok_or_else(|| anyhow!("invalid extension name"))?;
let mut extension_manifest_path = extension_dir.join("extension.json");
if fs.is_file(&extension_manifest_path).await {
let manifest_content = fs
.load(&extension_manifest_path)
.await
.with_context(|| format!("failed to load {extension_name} extension.json"))?;
let manifest_json = serde_json::from_str::<OldExtensionManifest>(&manifest_content)
.with_context(|| {
format!("invalid extension.json for extension {extension_name}")
})?;
Ok(manifest_from_old_manifest(manifest_json, extension_name))
} else {
extension_manifest_path.set_extension("toml");
let manifest_content = fs
.load(&extension_manifest_path)
.await
.with_context(|| format!("failed to load {extension_name} extension.toml"))?;
toml::from_str(&manifest_content)
.with_context(|| format!("invalid extension.json for extension {extension_name}"))
}
}
}
fn manifest_from_old_manifest(
manifest_json: OldExtensionManifest,
extension_id: &str,
) -> ExtensionManifest {
ExtensionManifest {
id: extension_id.into(),
name: manifest_json.name,
version: manifest_json.version,
description: manifest_json.description,
repository: manifest_json.repository,
authors: manifest_json.authors,
schema_version: 0,
lib: Default::default(),
themes: {
let mut themes = manifest_json.themes.into_values().collect::<Vec<_>>();
themes.sort();
themes.dedup();
themes
},
languages: {
let mut languages = manifest_json.languages.into_values().collect::<Vec<_>>();
languages.sort();
languages.dedup();
languages
},
grammars: manifest_json
.grammars
.into_keys()
.map(|grammar_name| (grammar_name, Default::default()))
.collect(),
language_servers: Default::default(),
}
}
fn load_plugin_queries(root_path: &Path) -> LanguageQueries {