zed_extension_api: Add github_release_by_tag_name (#12172)

This PR adds a new `github_release_by_tag_name` method to the
`zed_extension_api` to allow for retrieving a GitHub release by its tag
name.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-05-22 20:40:31 -04:00 committed by GitHub
parent 85ff80f3c0
commit 054c36cc29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 12 deletions

View File

@ -147,6 +147,24 @@ impl nodejs::Host for WasmState {
#[async_trait]
impl lsp::Host for WasmState {}
impl From<http::github::GithubRelease> for github::GithubRelease {
fn from(value: http::github::GithubRelease) -> Self {
Self {
version: value.tag_name,
assets: value.assets.into_iter().map(Into::into).collect(),
}
}
}
impl From<http::github::GithubReleaseAsset> for github::GithubReleaseAsset {
fn from(value: http::github::GithubReleaseAsset) -> Self {
Self {
name: value.name,
download_url: value.browser_download_url,
}
}
}
#[async_trait]
impl github::Host for WasmState {
async fn latest_github_release(
@ -162,17 +180,22 @@ impl github::Host for WasmState {
self.host.http_client.clone(),
)
.await?;
Ok(github::GithubRelease {
version: release.tag_name,
assets: release
.assets
.into_iter()
.map(|asset| github::GithubReleaseAsset {
name: asset.name,
download_url: asset.browser_download_url,
})
.collect(),
})
Ok(release.into())
})
.await
.to_wasmtime_result()
}
async fn github_release_by_tag_name(
&mut self,
repo: String,
tag: String,
) -> wasmtime::Result<Result<github::GithubRelease, String>> {
maybe!(async {
let release =
http::github::get_release_by_tag_name(&repo, &tag, self.host.http_client.clone())
.await?;
Ok(release.into())
})
.await
.to_wasmtime_result()

View File

@ -16,7 +16,8 @@ pub use serde_json;
pub use wit::{
download_file, make_file_executable,
zed::extension::github::{
latest_github_release, GithubRelease, GithubReleaseAsset, GithubReleaseOptions,
github_release_by_tag_name, latest_github_release, GithubRelease, GithubReleaseAsset,
GithubReleaseOptions,
},
zed::extension::nodejs::{
node_binary_path, npm_install_package, npm_package_installed_version,

View File

@ -25,4 +25,9 @@ interface github {
/// Returns the latest release for the given GitHub repository.
latest-github-release: func(repo: string, options: github-release-options) -> result<github-release, string>;
/// Returns the GitHub release with the specified tag name for the given GitHub repository.
///
/// Returns an error if a release with the given tag name does not exist.
github-release-by-tag-name: func(repo: string, tag: string) -> result<github-release, string>;
}

View File

@ -76,6 +76,47 @@ pub async fn latest_github_release(
.ok_or(anyhow!("Failed to find a release"))
}
pub async fn get_release_by_tag_name(
repo_name_with_owner: &str,
tag: &str,
http: Arc<dyn HttpClient>,
) -> Result<GithubRelease, anyhow::Error> {
let mut response = http
.get(
&format!("https://api.github.com/repos/{repo_name_with_owner}/releases/tags/{tag}"),
Default::default(),
true,
)
.await
.context("error fetching latest release")?;
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.context("error reading latest release")?;
if response.status().is_client_error() {
let text = String::from_utf8_lossy(body.as_slice());
bail!(
"status error {}, response: {text:?}",
response.status().as_u16()
);
}
let release = serde_json::from_slice::<GithubRelease>(body.as_slice()).map_err(|err| {
log::error!("Error deserializing: {:?}", err);
log::error!(
"GitHub API response text: {:?}",
String::from_utf8_lossy(body.as_slice())
);
anyhow!("error deserializing GitHub release")
})?;
Ok(release)
}
pub fn build_tarball_url(repo_name_with_owner: &str, tag: &str) -> Result<String> {
let mut url = Url::parse(&format!(
"https://github.com/{repo_name_with_owner}/archive/refs/tags",