#[cfg(any(not(debug_assertions), test))] use codex_install_context::InstallContext; #[cfg(any(not(debug_assertions), test))] use codex_install_context::StandalonePlatform; /// Update action the CLI should perform after the TUI exits. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UpdateAction { /// Update via `npm install -g @openai/codex@latest`. NpmGlobalLatest, /// Update via `bun install -g @openai/codex@latest`. BunGlobalLatest, /// Update via `brew upgrade codex`. BrewUpgrade, /// Update via `curl -fsSL https://chatgpt.com/codex/install.sh | sh`. StandaloneUnix, /// Update via `irm https://chatgpt.com/codex/install.ps1|iex`. StandaloneWindows, } impl UpdateAction { #[cfg(any(not(debug_assertions), test))] pub(crate) fn from_install_context(context: &InstallContext) -> Option { match context { InstallContext::Npm => Some(UpdateAction::NpmGlobalLatest), InstallContext::Bun => Some(UpdateAction::BunGlobalLatest), InstallContext::Brew => Some(UpdateAction::BrewUpgrade), InstallContext::Standalone { platform, .. } => Some(match platform { StandalonePlatform::Unix => UpdateAction::StandaloneUnix, StandalonePlatform::Windows => UpdateAction::StandaloneWindows, }), InstallContext::Other => None, } } /// Returns the list of command-line arguments for invoking the update. pub fn command_args(self) -> (&'static str, &'static [&'static str]) { match self { UpdateAction::NpmGlobalLatest => ("npm", &["install", "-g", "@openai/codex"]), UpdateAction::BunGlobalLatest => ("bun", &["install", "-g", "@openai/codex"]), UpdateAction::BrewUpgrade => ("brew", &["upgrade", "--cask", "codex"]), UpdateAction::StandaloneUnix => ( "sh", &["-c", "curl -fsSL https://chatgpt.com/codex/install.sh | sh"], ), UpdateAction::StandaloneWindows => ( "powershell", &["-c", "irm https://chatgpt.com/codex/install.ps1|iex"], ), } } /// Returns string representation of the command-line arguments for invoking the update. pub fn command_str(self) -> String { let (command, args) = self.command_args(); shlex::try_join(std::iter::once(command).chain(args.iter().copied())) .unwrap_or_else(|_| format!("{command} {}", args.join(" "))) } } #[cfg(not(debug_assertions))] pub(crate) fn get_update_action() -> Option { UpdateAction::from_install_context(InstallContext::current()) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; use std::path::PathBuf; #[test] fn maps_install_context_to_update_action() { let native_release_dir = PathBuf::from("/tmp/native-release"); assert_eq!( UpdateAction::from_install_context(&InstallContext::Other), None ); assert_eq!( UpdateAction::from_install_context(&InstallContext::Npm), Some(UpdateAction::NpmGlobalLatest) ); assert_eq!( UpdateAction::from_install_context(&InstallContext::Bun), Some(UpdateAction::BunGlobalLatest) ); assert_eq!( UpdateAction::from_install_context(&InstallContext::Brew), Some(UpdateAction::BrewUpgrade) ); assert_eq!( UpdateAction::from_install_context(&InstallContext::Standalone { platform: StandalonePlatform::Unix, release_dir: native_release_dir.clone(), resources_dir: Some(native_release_dir.join("codex-resources")), }), Some(UpdateAction::StandaloneUnix) ); assert_eq!( UpdateAction::from_install_context(&InstallContext::Standalone { platform: StandalonePlatform::Windows, release_dir: native_release_dir.clone(), resources_dir: Some(native_release_dir.join("codex-resources")), }), Some(UpdateAction::StandaloneWindows) ); } #[test] fn standalone_update_commands_rerun_latest_installer() { assert_eq!( UpdateAction::StandaloneUnix.command_args(), ( "sh", &["-c", "curl -fsSL https://chatgpt.com/codex/install.sh | sh"][..], ) ); assert_eq!( UpdateAction::StandaloneWindows.command_args(), ( "powershell", &["-c", "irm https://chatgpt.com/codex/install.ps1|iex"][..], ) ); } }