Restrict v0.0.7 of the `zed_extension_api` to dev builds, for now (#12170)

Marshall Bowers created

This PR restricts usage of v0.0.7 of the `zed_extension_api` to dev
builds, for now.

As we're still making changes to it, we don't want to ship a version of
Zed to Preview/Stable that claims to support a yet-unreleased version of
the extension API.

Release Notes:

- N/A

Change summary

Cargo.lock                                             |  2 +
crates/extension/Cargo.toml                            |  1 
crates/extension/src/extension_store.rs                | 12 ++++--
crates/extension/src/extension_store_test.rs           |  1 
crates/extension/src/wasm_host.rs                      | 12 +++++-
crates/extension/src/wasm_host/wit.rs                  | 21 +++++++++--
crates/extension/src/wasm_host/wit/since_v0_0_6.rs     |  1 
crates/extensions_ui/Cargo.toml                        |  1 
crates/extensions_ui/src/extension_version_selector.rs |  8 ++-
crates/extensions_ui/src/extensions_ui.rs              |  4 +
10 files changed, 48 insertions(+), 15 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3790,6 +3790,7 @@ dependencies = [
  "node_runtime",
  "parking_lot",
  "project",
+ "release_channel",
  "schemars",
  "semantic_version",
  "serde",
@@ -3844,6 +3845,7 @@ dependencies = [
  "language",
  "picker",
  "project",
+ "release_channel",
  "semantic_version",
  "serde",
  "settings",

crates/extension/Cargo.toml 🔗

@@ -30,6 +30,7 @@ log.workspace = true
 lsp.workspace = true
 node_runtime.workspace = true
 project.workspace = true
+release_channel.workspace = true
 schemars.workspace = true
 semantic_version.workspace = true
 serde.workspace = true

crates/extension/src/extension_store.rs 🔗

@@ -34,6 +34,7 @@ use language::{
 };
 use node_runtime::NodeRuntime;
 use project::ContextProviderWithTasks;
+use release_channel::ReleaseChannel;
 use semantic_version::SemanticVersion;
 use serde::{Deserialize, Serialize};
 use settings::Settings;
@@ -70,7 +71,10 @@ pub fn schema_version_range() -> RangeInclusive<SchemaVersion> {
 }
 
 /// Returns whether the given extension version is compatible with this version of Zed.
-pub fn is_version_compatible(extension_version: &ExtensionMetadata) -> bool {
+pub fn is_version_compatible(
+    release_channel: ReleaseChannel,
+    extension_version: &ExtensionMetadata,
+) -> bool {
     let schema_version = extension_version.manifest.schema_version.unwrap_or(0);
     if CURRENT_SCHEMA_VERSION.0 < schema_version {
         return false;
@@ -82,7 +86,7 @@ pub fn is_version_compatible(extension_version: &ExtensionMetadata) -> bool {
         .as_ref()
         .and_then(|wasm_api_version| SemanticVersion::from_str(wasm_api_version).ok())
     {
-        if !is_supported_wasm_api_version(wasm_api_version) {
+        if !is_supported_wasm_api_version(release_channel, wasm_api_version) {
             return false;
         }
     }
@@ -428,7 +432,7 @@ impl ExtensionStore {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Vec<ExtensionMetadata>>> {
         let schema_versions = schema_version_range();
-        let wasm_api_versions = wasm_api_version_range();
+        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
         let extension_settings = ExtensionSettings::get_global(cx);
         let extension_ids = self
             .extension_index
@@ -681,7 +685,7 @@ impl ExtensionStore {
         log::info!("installing extension {extension_id} latest version");
 
         let schema_versions = schema_version_range();
-        let wasm_api_versions = wasm_api_version_range();
+        let wasm_api_versions = wasm_api_version_range(ReleaseChannel::global(cx));
 
         let Some(url) = self
             .http_client

crates/extension/src/extension_store_test.rs 🔗

@@ -714,6 +714,7 @@ fn init_test(cx: &mut TestAppContext) {
     cx.update(|cx| {
         let store = SettingsStore::test(cx);
         cx.set_global(store);
+        release_channel::init("0.0.0", cx);
         theme::init(theme::LoadThemes::JustBase, cx);
         Project::init_settings(cx);
         ExtensionSettings::register(cx);

crates/extension/src/wasm_host.rs 🔗

@@ -16,6 +16,7 @@ use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
 use http::HttpClient;
 use language::LanguageRegistry;
 use node_runtime::NodeRuntime;
+use release_channel::ReleaseChannel;
 use semantic_version::SemanticVersion;
 use std::{
     path::{Path, PathBuf},
@@ -30,6 +31,7 @@ use wit::Extension;
 
 pub(crate) struct WasmHost {
     engine: Engine,
+    release_channel: ReleaseChannel,
     http_client: Arc<dyn HttpClient>,
     node_runtime: Arc<dyn NodeRuntime>,
     pub(crate) language_registry: Arc<LanguageRegistry>,
@@ -96,6 +98,7 @@ impl WasmHost {
             http_client,
             node_runtime,
             language_registry,
+            release_channel: ReleaseChannel::global(cx),
             _main_thread_message_task: task,
             main_thread_message_tx: tx,
         })
@@ -124,8 +127,13 @@ impl WasmHost {
                 },
             );
 
-            let (mut extension, instance) =
-                Extension::instantiate_async(&mut store, zed_api_version, &component).await?;
+            let (mut extension, instance) = Extension::instantiate_async(
+                &mut store,
+                this.release_channel,
+                zed_api_version,
+                &component,
+            )
+            .await?;
 
             extension
                 .call_init_extension(&mut store)

crates/extension/src/wasm_host/wit.rs 🔗

@@ -2,6 +2,7 @@ mod since_v0_0_1;
 mod since_v0_0_4;
 mod since_v0_0_6;
 mod since_v0_0_7;
+use release_channel::ReleaseChannel;
 use since_v0_0_7 as latest;
 
 use super::{wasm_engine, WasmState};
@@ -36,14 +37,23 @@ fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 }
 
 /// Returns whether the given Wasm API version is supported by the Wasm host.
-pub fn is_supported_wasm_api_version(version: SemanticVersion) -> bool {
-    wasm_api_version_range().contains(&version)
+pub fn is_supported_wasm_api_version(
+    release_channel: ReleaseChannel,
+    version: SemanticVersion,
+) -> bool {
+    wasm_api_version_range(release_channel).contains(&version)
 }
 
 /// Returns the Wasm API version range that is supported by the Wasm host.
 #[inline(always)]
-pub fn wasm_api_version_range() -> RangeInclusive<SemanticVersion> {
-    since_v0_0_1::MIN_VERSION..=latest::MAX_VERSION
+pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
+    let max_version = if release_channel == ReleaseChannel::Dev {
+        latest::MAX_VERSION
+    } else {
+        since_v0_0_6::MAX_VERSION
+    };
+
+    since_v0_0_1::MIN_VERSION..=max_version
 }
 
 pub enum Extension {
@@ -56,10 +66,11 @@ pub enum Extension {
 impl Extension {
     pub async fn instantiate_async(
         store: &mut Store<WasmState>,
+        release_channel: ReleaseChannel,
         version: SemanticVersion,
         component: &Component,
     ) -> Result<(Self, Instance)> {
-        if version >= latest::MIN_VERSION {
+        if release_channel == ReleaseChannel::Dev && version >= latest::MIN_VERSION {
             let (extension, instance) =
                 latest::Extension::instantiate_async(store, &component, latest::linker())
                     .await

crates/extension/src/wasm_host/wit/since_v0_0_6.rs 🔗

@@ -8,6 +8,7 @@ use std::sync::{Arc, OnceLock};
 use wasmtime::component::{Linker, Resource};
 
 pub const MIN_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 6);
+pub const MAX_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 6);
 
 wasmtime::component::bindgen!({
     async: true,

crates/extensions_ui/Cargo.toml 🔗

@@ -26,6 +26,7 @@ gpui.workspace = true
 language.workspace = true
 picker.workspace = true
 project.workspace = true
+release_channel.workspace = true
 semantic_version.workspace = true
 serde.workspace = true
 settings.workspace = true

crates/extensions_ui/src/extension_version_selector.rs 🔗

@@ -9,6 +9,7 @@ use gpui::{
     prelude::*, AppContext, DismissEvent, EventEmitter, FocusableView, Task, View, WeakView,
 };
 use picker::{Picker, PickerDelegate};
+use release_channel::ReleaseChannel;
 use semantic_version::SemanticVersion;
 use settings::update_settings_file;
 use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
@@ -166,7 +167,7 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         let candidate_id = self.matches[self.selected_index].candidate_id;
         let extension_version = &self.extension_versions[candidate_id];
 
-        if !extension::is_version_compatible(extension_version) {
+        if !extension::is_version_compatible(ReleaseChannel::global(cx), extension_version) {
             return;
         }
 
@@ -196,12 +197,13 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate {
         &self,
         ix: usize,
         selected: bool,
-        _cx: &mut ViewContext<Picker<Self>>,
+        cx: &mut ViewContext<Picker<Self>>,
     ) -> Option<Self::ListItem> {
         let version_match = &self.matches[ix];
         let extension_version = &self.extension_versions[version_match.candidate_id];
 
-        let is_version_compatible = extension::is_version_compatible(extension_version);
+        let is_version_compatible =
+            extension::is_version_compatible(ReleaseChannel::global(cx), extension_version);
         let disabled = !is_version_compatible;
 
         Some(

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -16,6 +16,7 @@ use gpui::{
     FontWeight, InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
     UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
 };
+use release_channel::ReleaseChannel;
 use settings::Settings;
 use std::ops::DerefMut;
 use std::time::Duration;
@@ -602,7 +603,8 @@ impl ExtensionsPage {
         has_dev_extension: bool,
         cx: &mut ViewContext<Self>,
     ) -> (Button, Option<Button>) {
-        let is_compatible = extension::is_version_compatible(&extension);
+        let is_compatible =
+            extension::is_version_compatible(ReleaseChannel::global(cx), &extension);
 
         if has_dev_extension {
             // If we have a dev extension for the given extension, just treat it as uninstalled.