mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
feat: This PR adds a first-party curl-based installer that downloads only the binaries needed for the current platform and wires it into Codex’s existing update UX.
It introduces a dedicated `installer/` folder for non-Rust install/update assets, a `CODEX_HOME`-aware on-disk layout, and a wrapper that makes helper CLIs available to Codex without globally polluting the user’s shell. ## Why Today the recommended install path is `npm i -g @openai/codex`, but the npm package bundles native binaries for all platforms (and `rg`), leading to very large installs (hundreds of MB unpacked) even though a single platform binary is much smaller. Homebrew avoids this on macOS by downloading only the matching artifact. This PR brings that same property to a cross-platform one-liner: ```sh curl -fsSL https://raw.githubusercontent.com/openai/codex/main/installer/install.sh | bash ``` Key goals: - Download only the user’s platform artifacts. - Keep curl-managed installs isolated under `CODEX_HOME`. - Preserve compatibility with npm/brew installs (shadowing is fine, breaking is not). - Provide a clean place to add helper CLIs that should be available when Codex runs. ## How It Works ### Install root and layout All curl-managed artifacts live under `CODEX_HOME` (default: `~/.codex`). The layout is: - `CODEX_HOME/bin/` - `CODEX_HOME/versions/<version>/bin/` - `CODEX_HOME/versions/current` (symlink) - `CODEX_HOME/tools/bin/` This design supports atomic version switches and helper CLIs: - The user’s global `PATH` only needs `CODEX_HOME/bin`. - The runtime `PATH` is extended inside the wrapper to include helper CLIs. See `installer/README.md:1`. ### Installer scripts #### `installer/install.sh` `installer/install.sh:1` is the public entrypoint: - Requires Bash explicitly (we use Bash features like `mapfile`). - Loads `installer/lib.sh` locally when run from a checkout, or downloads it when piped from curl. - Detects `arch`/`os`, resolves the version (via `CODEX_VERSION` or GitHub Releases), downloads the correct tarball, installs it, activates it, and updates the user’s rc file. #### `installer/lib.sh` `installer/lib.sh:10` centralizes the mechanics: - Resolves `CODEX_HOME` with a fallback to `~/.codex`. - Detects OS/arch and builds release URLs. - Idempotently updates the user rc file via a marker block that respects non-default `CODEX_HOME` values: `installer/lib.sh:106`. - Installs a wrapper at `CODEX_HOME/bin/codex` that: - Sets `CODEX_MANAGED_BY_CURL=1`. - Extends `PATH` at runtime to include: - `CODEX_HOME/bin` - `CODEX_HOME/tools/bin` - `CODEX_HOME/versions/current/bin` - Execs the active versioned binary: `installer/lib.sh:154`. - Installs the tarball into a versioned `bin/` directory and preserves any additional CLIs present in the tarball (not just `codex`): `installer/lib.sh:181`. - Keeps the last two installed versions and avoids deleting the `current` symlink: `installer/lib.sh:239`. #### `installer/update.sh` `installer/update.sh:1` mirrors the installer flow but always targets the latest release. ### Update UX integration in the CLI Codex’s update prompt already knows how to run a package-manager-specific update command after the TUI exits. This PR adds a curl-managed update action in `codex-rs/tui/src/update_action.rs:3`: - Adds `UpdateAction::CurlInstallerUpdate`. - Detects curl-managed installs via `CODEX_MANAGED_BY_CURL` (set by the wrapper): `codex-rs/tui/src/update_action.rs:40`. - Uses the curl updater one-liner for the update command: `codex-rs/tui/src/update_action.rs:21`. This keeps update behavior consistent with npm/brew while avoiding fragile path heuristics. ## Docs updates - The curl installer is now the first recommended install path in `README.md:1` and `README.md:18`. - `docs/install.md:11` adds a curl install section and points to `installer/README.md` for the detailed mechanics. ## Compatibility and safety notes - This does not modify npm or Homebrew installs. - The rc-file change only adds `CODEX_HOME/bin` to `PATH`. - Additional helper CLIs are available to Codex via the wrapper’s runtime `PATH`, rather than being forced into the user’s global shell. - The installer honors `CODEX_HOME` everywhere, falling back to `~/.codex`. ## Testing Commands run: ```sh cd codex-rs just fmt cargo test -p codex-tui ``` Notes: - In this environment, `cargo test -p codex-tui` required running outside the sandbox due to a macOS `SystemConfiguration` panic; it passed once run with escalated permissions. - No new VS Code diagnostics were reported. ## Follow-ups (explicitly not in this PR) - Ensure the release tarballs include any helper CLIs we expect to be available at runtime (for example, Windows sandbox helpers). - Add an explicit `codex self-update` command that delegates to `installer/update.sh`. - Decide how we want to manage third-party tools like `rg` under `CODEX_HOME/tools/` and whether the CLI should probe a managed fallback path.
This commit is contained in:
@@ -7,6 +7,8 @@ pub enum UpdateAction {
|
||||
BunGlobalLatest,
|
||||
/// Update via `brew upgrade codex`.
|
||||
BrewUpgrade,
|
||||
/// Update via the curl installer.
|
||||
CurlInstallerUpdate,
|
||||
}
|
||||
|
||||
impl UpdateAction {
|
||||
@@ -16,6 +18,13 @@ impl UpdateAction {
|
||||
UpdateAction::NpmGlobalLatest => ("npm", &["install", "-g", "@openai/codex"]),
|
||||
UpdateAction::BunGlobalLatest => ("bun", &["install", "-g", "@openai/codex"]),
|
||||
UpdateAction::BrewUpgrade => ("brew", &["upgrade", "codex"]),
|
||||
UpdateAction::CurlInstallerUpdate => (
|
||||
"bash",
|
||||
&[
|
||||
"-c",
|
||||
"set -euo pipefail; curl -fsSL https://raw.githubusercontent.com/openai/codex/main/installer/update.sh | bash",
|
||||
],
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +41,14 @@ pub(crate) fn get_update_action() -> Option<UpdateAction> {
|
||||
let exe = std::env::current_exe().unwrap_or_default();
|
||||
let managed_by_npm = std::env::var_os("CODEX_MANAGED_BY_NPM").is_some();
|
||||
let managed_by_bun = std::env::var_os("CODEX_MANAGED_BY_BUN").is_some();
|
||||
let managed_by_curl = std::env::var_os("CODEX_MANAGED_BY_CURL").is_some();
|
||||
|
||||
detect_update_action(
|
||||
cfg!(target_os = "macos"),
|
||||
&exe,
|
||||
managed_by_npm,
|
||||
managed_by_bun,
|
||||
managed_by_curl,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,11 +58,14 @@ fn detect_update_action(
|
||||
current_exe: &std::path::Path,
|
||||
managed_by_npm: bool,
|
||||
managed_by_bun: bool,
|
||||
managed_by_curl: bool,
|
||||
) -> Option<UpdateAction> {
|
||||
if managed_by_npm {
|
||||
Some(UpdateAction::NpmGlobalLatest)
|
||||
} else if managed_by_bun {
|
||||
Some(UpdateAction::BunGlobalLatest)
|
||||
} else if managed_by_curl {
|
||||
Some(UpdateAction::CurlInstallerUpdate)
|
||||
} else if is_macos
|
||||
&& (current_exe.starts_with("/opt/homebrew") || current_exe.starts_with("/usr/local"))
|
||||
{
|
||||
@@ -68,15 +82,21 @@ mod tests {
|
||||
#[test]
|
||||
fn detects_update_action_without_env_mutation() {
|
||||
assert_eq!(
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), false, false),
|
||||
detect_update_action(
|
||||
false,
|
||||
std::path::Path::new("/any/path"),
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), true, false),
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), true, false, false),
|
||||
Some(UpdateAction::NpmGlobalLatest)
|
||||
);
|
||||
assert_eq!(
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), false, true),
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), false, true, false),
|
||||
Some(UpdateAction::BunGlobalLatest)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -84,7 +104,8 @@ mod tests {
|
||||
true,
|
||||
std::path::Path::new("/opt/homebrew/bin/codex"),
|
||||
false,
|
||||
false
|
||||
false,
|
||||
false,
|
||||
),
|
||||
Some(UpdateAction::BrewUpgrade)
|
||||
);
|
||||
@@ -93,9 +114,14 @@ mod tests {
|
||||
true,
|
||||
std::path::Path::new("/usr/local/bin/codex"),
|
||||
false,
|
||||
false
|
||||
false,
|
||||
false,
|
||||
),
|
||||
Some(UpdateAction::BrewUpgrade)
|
||||
);
|
||||
assert_eq!(
|
||||
detect_update_action(false, std::path::Path::new("/any/path"), false, false, true),
|
||||
Some(UpdateAction::CurlInstallerUpdate)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user