Detect new releases of codex-acp (#39388)
Now we use GitHub Releases to detect when there's a new version of codex-acp out, and we notify the user in the same way we do for the other external agents. This also moves `github_download.rs` out of the `languages` crate and into `http_client`, because now we're not just using it for language servers anymore, we're also using it for external agents. Release Notes: - N/A --------- Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
This commit is contained in:
co-authored by
Lukas Wirth
parent
3ab5103de1
commit
662ec9977f
@@ -16,6 +16,7 @@ use futures::StreamExt as _;
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
|
||||
};
|
||||
use http_client::github::AssetKind;
|
||||
use node_runtime::NodeRuntime;
|
||||
use remote::RemoteClient;
|
||||
use rpc::{AnyProtoClient, TypedEnvelope, proto};
|
||||
@@ -1022,7 +1023,7 @@ impl ExternalAgentServer for LocalCodex {
|
||||
// Find or install the latest Codex release (no update checks for now).
|
||||
let http = cx.update(|cx| Client::global(cx).http_client())?;
|
||||
let release = ::http_client::github::latest_github_release(
|
||||
"zed-industries/codex-acp",
|
||||
CODEX_ACP_REPO,
|
||||
true,
|
||||
false,
|
||||
http.clone(),
|
||||
@@ -1032,42 +1033,27 @@ impl ExternalAgentServer for LocalCodex {
|
||||
|
||||
let version_dir = dir.join(&release.tag_name);
|
||||
if !fs.is_dir(&version_dir).await {
|
||||
// Assemble release download URL from prefix, tag, and filename based on target triple.
|
||||
// If unsupported, silently skip download.
|
||||
let tag = release.tag_name.clone(); // e.g. "v0.1.0"
|
||||
let tag = release.tag_name.clone();
|
||||
let version_number = tag.trim_start_matches('v');
|
||||
if let Some(asset_url) = codex_release_url(version_number) {
|
||||
let http = http.clone();
|
||||
let mut response = http
|
||||
.get(&asset_url, Default::default(), true)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("downloading Codex binary from {}", asset_url)
|
||||
})?;
|
||||
anyhow::ensure!(
|
||||
response.status().is_success(),
|
||||
"failed to download Codex release: {}",
|
||||
response.status()
|
||||
);
|
||||
|
||||
// Extract archive into the version directory.
|
||||
if asset_url.ends_with(".zip") {
|
||||
let reader = futures::io::BufReader::new(response.body_mut());
|
||||
util::archive::extract_zip(&version_dir, reader)
|
||||
.await
|
||||
.context("extracting Codex binary from zip")?;
|
||||
let asset_name = asset_name(version_number)
|
||||
.context("codex acp is not supported for this architecture")?;
|
||||
let asset = release
|
||||
.assets
|
||||
.into_iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||
::http_client::github_download::download_server_binary(
|
||||
&*http,
|
||||
&asset.browser_download_url,
|
||||
asset.digest.as_deref(),
|
||||
&version_dir,
|
||||
if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
|
||||
AssetKind::Zip
|
||||
} else {
|
||||
// Decompress and extract the tar.gz into the version directory.
|
||||
let reader = futures::io::BufReader::new(response.body_mut());
|
||||
let decoder =
|
||||
async_compression::futures::bufread::GzipDecoder::new(reader);
|
||||
let archive = async_tar::Archive::new(decoder);
|
||||
archive
|
||||
.unpack(&version_dir)
|
||||
.await
|
||||
.context("extracting Codex binary from tar.gz")?;
|
||||
}
|
||||
}
|
||||
AssetKind::TarGz
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let bin_name = if cfg!(windows) {
|
||||
@@ -1101,11 +1087,13 @@ impl ExternalAgentServer for LocalCodex {
|
||||
}
|
||||
}
|
||||
|
||||
pub const CODEX_ACP_REPO: &str = "zed-industries/codex-acp";
|
||||
|
||||
/// Assemble Codex release URL for the current OS/arch and the given version number.
|
||||
/// Returns None if the current target is unsupported.
|
||||
/// Example output:
|
||||
/// https://github.com/zed-industries/codex-acp/releases/download/v{version}/codex-acp-{version}-{arch}-{platform}.{ext}
|
||||
fn codex_release_url(version: &str) -> Option<String> {
|
||||
fn asset_name(version: &str) -> Option<String> {
|
||||
let arch = if cfg!(target_arch = "x86_64") {
|
||||
"x86_64"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
@@ -1131,11 +1119,7 @@ fn codex_release_url(version: &str) -> Option<String> {
|
||||
"tar.gz"
|
||||
};
|
||||
|
||||
let prefix = "https://github.com/zed-industries/codex-acp/releases/download";
|
||||
|
||||
Some(format!(
|
||||
"{prefix}/v{version}/codex-acp-{version}-{arch}-{platform}.{ext}"
|
||||
))
|
||||
Some(format!("codex-acp-{version}-{arch}-{platform}.{ext}"))
|
||||
}
|
||||
|
||||
struct LocalCustomAgent {
|
||||
@@ -1191,18 +1175,18 @@ mod tests {
|
||||
// Additionally, it verifies that our logic for assembling URLs
|
||||
// correctly resolves to a known-good URL on each of our targets.
|
||||
let allowed = [
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-aarch64-apple-darwin.tar.gz",
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-aarch64-pc-windows-msvc.tar.gz",
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-aarch64-unknown-linux-gnu.tar.gz",
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-x86_64-apple-darwin.tar.gz",
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-x86_64-pc-windows-msvc.zip",
|
||||
"https://github.com/zed-industries/codex-acp/releases/download/v0.1.0/codex-acp-0.1.0-x86_64-unknown-linux-gnu.tar.gz",
|
||||
"codex-acp-0.1.0-aarch64-apple-darwin.tar.gz",
|
||||
"codex-acp-0.1.0-aarch64-pc-windows-msvc.tar.gz",
|
||||
"codex-acp-0.1.0-aarch64-unknown-linux-gnu.tar.gz",
|
||||
"codex-acp-0.1.0-x86_64-apple-darwin.tar.gz",
|
||||
"codex-acp-0.1.0-x86_64-pc-windows-msvc.zip",
|
||||
"codex-acp-0.1.0-x86_64-unknown-linux-gnu.tar.gz",
|
||||
];
|
||||
|
||||
if let Some(url) = super::codex_release_url(version_number) {
|
||||
if let Some(url) = super::asset_name(version_number) {
|
||||
assert!(
|
||||
allowed.contains(&url.as_str()),
|
||||
"Assembled URL {} not in allowed list",
|
||||
"Assembled asset name {} not in allowed list",
|
||||
url
|
||||
);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user