extension_api: Add `digest` to `GithubReleaseAsset` (#48413)

Finn Evers and Zed Zippy created

Round 2 of #46269 with conflicts properly resolved.

This came up a few times across various extensions. With us about to
ship a new API version soon, it might be worth to add.

Release Notes:

- N/A

---------

Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>

Change summary

crates/extension_api/wit/since_v0.8.0/github.wit        |  2 
crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs |  6 
crates/extension_host/src/wasm_host/wit/since_v0_0_6.rs |  4 
crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs |  4 
crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs |  2 
crates/extension_host/src/wasm_host/wit/since_v0_3_0.rs |  2 
crates/extension_host/src/wasm_host/wit/since_v0_4_0.rs |  2 
crates/extension_host/src/wasm_host/wit/since_v0_5_0.rs |  2 
crates/extension_host/src/wasm_host/wit/since_v0_6_0.rs | 56 ++++++++++
crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs |  1 
crates/project/src/agent_server_store.rs                |  4 
11 files changed, 68 insertions(+), 17 deletions(-)

Detailed changes

crates/extension_api/wit/since_v0.8.0/github.wit 🔗

@@ -13,6 +13,8 @@ interface github {
         name: string,
         /// The download URL for the asset.
         download-url: string,
+        /// The SHA-256 of the release asset if provided by the GitHub API.
+        digest: option<string>,
     }
 
     /// The options used to filter down GitHub releases.

crates/extension_host/src/wasm_host/wit/since_v0_0_1.rs 🔗

@@ -1,4 +1,4 @@
-use super::latest;
+use super::{latest, since_v0_6_0};
 use crate::wasm_host::WasmState;
 use crate::wasm_host::wit::since_v0_0_4;
 use anyhow::Result;
@@ -17,7 +17,7 @@ wasmtime::component::bindgen!({
     path: "../extension_api/wit/since_v0.0.1",
     with: {
          "worktree": ExtensionWorktree,
-         "zed:extension/github": latest::zed::extension::github,
+         "zed:extension/github": since_v0_6_0::zed::extension::github,
          "zed:extension/platform": latest::zed::extension::platform,
     },
 });
@@ -120,7 +120,7 @@ impl ExtensionImports for WasmState {
         repo: String,
         options: GithubReleaseOptions,
     ) -> wasmtime::Result<Result<GithubRelease, String>> {
-        latest::zed::extension::github::Host::latest_github_release(self, repo, options).await
+        since_v0_6_0::zed::extension::github::Host::latest_github_release(self, repo, options).await
     }
 
     async fn current_platform(&mut self) -> Result<(Os, Architecture)> {

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

@@ -1,4 +1,4 @@
-use super::{latest, since_v0_1_0};
+use super::{latest, since_v0_1_0, since_v0_6_0};
 use crate::wasm_host::WasmState;
 use anyhow::Result;
 use extension::WorktreeDelegate;
@@ -15,7 +15,7 @@ wasmtime::component::bindgen!({
     path: "../extension_api/wit/since_v0.0.6",
     with: {
          "worktree": ExtensionWorktree,
-         "zed:extension/github": latest::zed::extension::github,
+         "zed:extension/github": since_v0_6_0::zed::extension::github,
          "zed:extension/lsp": since_v0_1_0::zed::extension::lsp,
          "zed:extension/nodejs": latest::zed::extension::nodejs,
          "zed:extension/platform": latest::zed::extension::platform,

crates/extension_host/src/wasm_host/wit/since_v0_1_0.rs 🔗

@@ -21,7 +21,7 @@ use util::rel_path::RelPath;
 use util::{archive::extract_zip, fs::make_file_executable, maybe};
 use wasmtime::component::{Linker, Resource};
 
-use super::latest;
+use super::{latest, since_v0_6_0};
 
 pub const MIN_VERSION: Version = Version::new(0, 1, 0);
 
@@ -33,7 +33,7 @@ wasmtime::component::bindgen!({
          "worktree": ExtensionWorktree,
          "key-value-store": ExtensionKeyValueStore,
          "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream,
-         "zed:extension/github": latest::zed::extension::github,
+         "zed:extension/github": since_v0_6_0::zed::extension::github,
          "zed:extension/nodejs": latest::zed::extension::nodejs,
          "zed:extension/platform": latest::zed::extension::platform,
          "zed:extension/slash-command": latest::zed::extension::slash_command,

crates/extension_host/src/wasm_host/wit/since_v0_2_0.rs 🔗

@@ -18,7 +18,7 @@ wasmtime::component::bindgen!({
          "worktree": ExtensionWorktree,
          "project": ExtensionProject,
          "key-value-store": ExtensionKeyValueStore,
-         "zed:extension/github": latest::zed::extension::github,
+         "zed:extension/github": since_v0_6_0::zed::extension::github,
          "zed:extension/http-client": latest::zed::extension::http_client,
          "zed:extension/lsp": since_v0_6_0::zed::extension::lsp,
          "zed:extension/nodejs": latest::zed::extension::nodejs,

crates/extension_host/src/wasm_host/wit/since_v0_3_0.rs 🔗

@@ -19,7 +19,7 @@ wasmtime::component::bindgen!({
          "project": ExtensionProject,
          "key-value-store": ExtensionKeyValueStore,
          "zed:extension/common": latest::zed::extension::common,
-         "zed:extension/github": latest::zed::extension::github,
+         "zed:extension/github": since_v0_6_0::zed::extension::github,
          "zed:extension/http-client": latest::zed::extension::http_client,
          "zed:extension/lsp": since_v0_6_0::zed::extension::lsp,
          "zed:extension/nodejs": latest::zed::extension::nodejs,

crates/extension_host/src/wasm_host/wit/since_v0_4_0.rs 🔗

@@ -19,7 +19,7 @@ wasmtime::component::bindgen!({
         "project": ExtensionProject,
         "key-value-store": ExtensionKeyValueStore,
         "zed:extension/common": latest::zed::extension::common,
-        "zed:extension/github": latest::zed::extension::github,
+        "zed:extension/github": since_v0_6_0::zed::extension::github,
         "zed:extension/http-client": latest::zed::extension::http_client,
         "zed:extension/lsp": since_v0_6_0::zed::extension::lsp,
         "zed:extension/nodejs": latest::zed::extension::nodejs,

crates/extension_host/src/wasm_host/wit/since_v0_5_0.rs 🔗

@@ -19,7 +19,7 @@ wasmtime::component::bindgen!({
         "project": ExtensionProject,
         "key-value-store": ExtensionKeyValueStore,
         "zed:extension/common": latest::zed::extension::common,
-        "zed:extension/github": latest::zed::extension::github,
+        "zed:extension/github": since_v0_6_0::zed::extension::github,
         "zed:extension/http-client": latest::zed::extension::http_client,
         "zed:extension/lsp": since_v0_6_0::zed::extension::lsp,
         "zed:extension/nodejs": latest::zed::extension::nodejs,

crates/extension_host/src/wasm_host/wit/since_v0_6_0.rs 🔗

@@ -20,7 +20,6 @@ wasmtime::component::bindgen!({
         "project": ExtensionProject,
         "key-value-store": ExtensionKeyValueStore,
         "zed:extension/common": latest::zed::extension::common,
-        "zed:extension/github": latest::zed::extension::github,
         "zed:extension/http-client": latest::zed::extension::http_client,
         "zed:extension/nodejs": latest::zed::extension::nodejs,
         "zed:extension/platform": latest::zed::extension::platform,
@@ -106,15 +105,55 @@ impl From<DownloadedFileType> for latest::DownloadedFileType {
     }
 }
 
-impl From<latest::lsp::Symbol> for lsp::Symbol {
-    fn from(value: latest::lsp::Symbol) -> Self {
+impl From<latest::github::GithubReleaseAsset> for github::GithubReleaseAsset {
+    fn from(value: latest::github::GithubReleaseAsset) -> Self {
         Self {
             name: value.name,
-            kind: value.kind.into(),
+            download_url: value.download_url,
+        }
+    }
+}
+
+impl From<latest::github::GithubRelease> for github::GithubRelease {
+    fn from(value: latest::github::GithubRelease) -> Self {
+        Self {
+            version: value.version,
+            assets: value.assets.into_iter().map(Into::into).collect(),
         }
     }
 }
 
+impl From<github::GithubReleaseOptions> for latest::github::GithubReleaseOptions {
+    fn from(value: github::GithubReleaseOptions) -> Self {
+        Self {
+            require_assets: value.require_assets,
+            pre_release: value.pre_release,
+        }
+    }
+}
+
+impl zed::extension::github::Host for WasmState {
+    async fn github_release_by_tag_name(
+        &mut self,
+        repo: String,
+        tag: String,
+    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
+        latest::github::Host::github_release_by_tag_name(self, repo, tag)
+            .await
+            .map(|result| result.map(Into::into))
+    }
+
+    async fn latest_github_release(
+        &mut self,
+        repo: String,
+        options: github::GithubReleaseOptions,
+    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
+        latest::github::Host::latest_github_release(self, repo, options.into())
+            .await
+            .map(|result| result.map(Into::into))
+    }
+}
+
 impl From<latest::lsp::Completion> for lsp::Completion {
     fn from(value: latest::lsp::Completion) -> Self {
         Self {
@@ -127,6 +166,15 @@ impl From<latest::lsp::Completion> for lsp::Completion {
     }
 }
 
+impl From<latest::lsp::Symbol> for lsp::Symbol {
+    fn from(value: latest::lsp::Symbol) -> Self {
+        Self {
+            name: value.name,
+            kind: value.kind.into(),
+        }
+    }
+}
+
 impl From<latest::lsp::CompletionLabelDetails> for lsp::CompletionLabelDetails {
     fn from(value: latest::lsp::CompletionLabelDetails) -> Self {
         Self {

crates/project/src/agent_server_store.rs 🔗

@@ -1836,10 +1836,10 @@ impl ExternalAgentServer for LocalExtensionArchiveAgent {
                                     release.assets.iter().find(|a| a.name == filename)
                                 {
                                     // Strip "sha256:" prefix if present
-                                    asset.digest.as_ref().and_then(|d| {
+                                    asset.digest.as_ref().map(|d| {
                                         d.strip_prefix("sha256:")
                                             .map(|s| s.to_string())
-                                            .or_else(|| Some(d.clone()))
+                                            .unwrap_or_else(|| d.clone())
                                     })
                                 } else {
                                     None