Codex Handbook
tui/src/update_action.rs 165 lines
#[cfg(any(not(debug_assertions), test))]use codex_install_context::InstallContext;#[cfg(any(not(debug_assertions), test))]use codex_install_context::InstallMethod;#[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 | CODEX_NON_INTERACTIVE=1 sh`.    StandaloneUnix,    /// Update via `$env:CODEX_NON_INTERACTIVE=1; 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<Self> {        match &context.method {            InstallMethod::Npm => Some(UpdateAction::NpmGlobalLatest),            InstallMethod::Bun => Some(UpdateAction::BunGlobalLatest),            InstallMethod::Brew => Some(UpdateAction::BrewUpgrade),            InstallMethod::Standalone { platform, .. } => Some(match platform {                StandalonePlatform::Unix => UpdateAction::StandaloneUnix,                StandalonePlatform::Windows => UpdateAction::StandaloneWindows,            }),            InstallMethod::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 | CODEX_NON_INTERACTIVE=1 sh",                ],            ),            UpdateAction::StandaloneWindows => (                "powershell",                &[                    "-ExecutionPolicy",                    "Bypass",                    "-c",                    "$env:CODEX_NON_INTERACTIVE=1; 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 fn get_update_action() -> Option<UpdateAction> {    UpdateAction::from_install_context(InstallContext::current())}#[cfg(test)]mod tests {    use super::*;    use codex_utils_absolute_path::AbsolutePathBuf;    use pretty_assertions::assert_eq;    #[test]    fn maps_install_context_to_update_action() {        let native_release_dir =            AbsolutePathBuf::from_absolute_path(std::env::temp_dir().join("native-release"))                .expect("temp dir path should be absolute");        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Other,                package_layout: None,            }),            None        );        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Npm,                package_layout: None,            }),            Some(UpdateAction::NpmGlobalLatest)        );        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Bun,                package_layout: None,            }),            Some(UpdateAction::BunGlobalLatest)        );        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Brew,                package_layout: None,            }),            Some(UpdateAction::BrewUpgrade)        );        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Standalone {                    platform: StandalonePlatform::Unix,                    release_dir: native_release_dir.clone(),                    resources_dir: Some(native_release_dir.join("codex-resources")),                },                package_layout: None,            }),            Some(UpdateAction::StandaloneUnix)        );        assert_eq!(            UpdateAction::from_install_context(&InstallContext {                method: InstallMethod::Standalone {                    platform: StandalonePlatform::Windows,                    release_dir: native_release_dir.clone(),                    resources_dir: Some(native_release_dir.join("codex-resources")),                },                package_layout: None,            }),            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 | CODEX_NON_INTERACTIVE=1 sh"                ][..],            )        );        assert_eq!(            UpdateAction::StandaloneWindows.command_args(),            (                "powershell",                &[                    "-ExecutionPolicy",                    "Bypass",                    "-c",                    "$env:CODEX_NON_INTERACTIVE=1; irm https://chatgpt.com/codex/install.ps1 | iex"                ][..],            )        );    }}