This commit is contained in:
jif-oai
2025-10-29 12:31:44 +00:00
parent 00dad73abd
commit 5544ec8cc6
6 changed files with 81 additions and 21 deletions

1
codex-rs/Cargo.lock generated
View File

@@ -1460,6 +1460,7 @@ dependencies = [
"codex-ansi-escape",
"codex-app-server-protocol",
"codex-arg0",
"codex-auto-updater",
"codex-common",
"codex-core",
"codex-feedback",

View File

@@ -60,6 +60,7 @@ codex-app-server = { path = "app-server" }
codex-app-server-protocol = { path = "app-server-protocol" }
codex-apply-patch = { path = "apply-patch" }
codex-arg0 = { path = "arg0" }
codex-auto-updater = { path = "auto-updater" }
codex-async-utils = { path = "async-utils" }
codex-backend-client = { path = "backend-client" }
codex-chatgpt = { path = "chatgpt" }

View File

@@ -1,4 +1,5 @@
use crate::Installer;
use crate::UpdateStatus;
use crate::errors::Error;
use async_trait::async_trait;
use semver::Version;
@@ -23,14 +24,14 @@ impl BrewInstaller {
};
let installer = Self { path };
match installer.status() {
match installer.install_status() {
Ok(_) => Ok(Some(installer)),
Err(Error::Unsupported) => Ok(None),
Err(err) => Err(err),
}
}
fn status(&self) -> Result<InstallStatus, Error> {
fn install_status(&self) -> Result<InstallStatus, Error> {
if let Some(info) = self.formula_info()? {
let current_version = self.formula_current_version()?;
return Ok(InstallStatus {
@@ -126,27 +127,39 @@ impl BrewInstaller {
#[async_trait]
impl Installer for BrewInstaller {
fn update_available(&self) -> Result<bool, Error> {
let status = self.status()?;
status.needs_update()
fn version_status(&self) -> Result<UpdateStatus, Error> {
let status = self.install_status()?;
let update_available = status.needs_update()?;
let InstallStatus {
method: _,
current_version,
latest_version,
} = status;
Ok(UpdateStatus {
current_version,
latest_version,
update_available,
})
}
async fn update(&self) -> Result<String, Error> {
let initial_status = run_blocking({
let brew = self.clone();
move || brew.status()
move || brew.install_status()
})
.await?;
if !initial_status.needs_update()? {
let needs_update = initial_status.needs_update()?;
let method = initial_status.method;
if !needs_update {
return Ok(initial_status.current_version);
}
self.upgrade(initial_status.method).await?;
self.upgrade(method).await?;
run_blocking({
let brew = self.clone();
move || brew.current_version(initial_status.method)
move || brew.current_version(method)
})
.await
}
@@ -482,6 +495,19 @@ esac
Ok(())
}
#[tokio::test]
async fn update_status_reports_versions() -> Result<(), Box<dyn StdError>> {
let fake_brew = FakeBrew::formula("0.8.0", "0.9.0", "0.9.0")?;
let status = crate::update_status()?;
pretty_assertions::assert_eq!(status.update_available, true);
pretty_assertions::assert_eq!(status.current_version, "0.8.0".to_string());
pretty_assertions::assert_eq!(status.latest_version, "0.9.0".to_string());
drop(fake_brew);
Ok(())
}
#[tokio::test]
async fn update_executes_formula_upgrade() -> Result<(), Box<dyn StdError>> {
let fake_brew = FakeBrew::formula("0.8.0", "0.9.0", "0.9.0")?;
@@ -644,17 +670,15 @@ esac
fs::write(&upgrade_log, Vec::new())?;
let env = EnvironmentGuard::new(tempdir.path());
let mut vars = Vec::new();
vars.push(VarGuard::new("BREW_FORMULA_INFO", &formula_info_path));
vars.push(VarGuard::new("BREW_CASK_INFO", &cask_info_path));
vars.push(VarGuard::new("BREW_FORMULA_LIST", &formula_list_path));
vars.push(VarGuard::new("BREW_CASK_LIST", &cask_list_path));
vars.push(VarGuard::new(
"BREW_FORMULA_UPDATED_LIST",
&formula_updated_path,
));
vars.push(VarGuard::new("BREW_CASK_UPDATED_LIST", &cask_updated_path));
vars.push(VarGuard::new("BREW_UPGRADE_LOG", &upgrade_log));
let vars = vec![
VarGuard::new("BREW_FORMULA_INFO", &formula_info_path),
VarGuard::new("BREW_CASK_INFO", &cask_info_path),
VarGuard::new("BREW_FORMULA_LIST", &formula_list_path),
VarGuard::new("BREW_CASK_LIST", &cask_list_path),
VarGuard::new("BREW_FORMULA_UPDATED_LIST", &formula_updated_path),
VarGuard::new("BREW_CASK_UPDATED_LIST", &cask_updated_path),
VarGuard::new("BREW_UPGRADE_LOG", &upgrade_log),
];
Ok(Self {
_tempdir: tempdir,

View File

@@ -6,9 +6,20 @@ pub use errors::Error;
use crate::brew::BrewInstaller;
#[derive(Debug)]
pub struct UpdateStatus {
pub current_version: String,
pub latest_version: String,
pub update_available: bool,
}
#[async_trait]
pub trait Installer: Send + Sync {
fn update_available(&self) -> Result<bool, Error>;
fn version_status(&self) -> Result<UpdateStatus, Error>;
fn update_available(&self) -> Result<bool, Error> {
self.version_status().map(|status| status.update_available)
}
async fn update(&self) -> Result<String, Error>;
}
@@ -20,6 +31,10 @@ pub fn installer() -> Result<Box<dyn Installer>, Error> {
Err(Error::Unsupported)
}
pub fn update_status() -> Result<UpdateStatus, Error> {
installer()?.version_status()
}
pub fn update_available() -> Result<bool, Error> {
installer()?.update_available()
}

View File

@@ -33,6 +33,7 @@ codex-common = { workspace = true, features = [
"elapsed",
"sandbox_summary",
] }
codex-auto-updater = { workspace = true }
codex-core = { workspace = true }
codex-file-search = { workspace = true }
codex-login = { workspace = true }

View File

@@ -7,6 +7,8 @@ use additional_dirs::add_dir_warning_message;
use app::App;
pub use app::AppExitInfo;
use codex_app_server_protocol::AuthMode;
use codex_auto_updater::Error as AutoUpdateError;
use codex_auto_updater::update_status;
use codex_core::AuthManager;
use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID;
use codex_core::CodexAuth;
@@ -256,6 +258,22 @@ pub async fn run_main(
.try_init();
};
match update_status() {
Ok(status) if status.update_available => {
let current = status.current_version;
let latest = status.latest_version;
tracing::error!(
current_version = current.as_str(),
latest_version = latest.as_str(),
"A newer Codex release is available. Update Codex from {current} to {latest} with `brew upgrade codex`."
);
}
Ok(_) | Err(AutoUpdateError::Unsupported) => {}
Err(err) => {
tracing::debug!(error = ?err, "Failed to check for Codex updates");
}
}
run_ratatui_app(
cli,
config,