4.9 KiB
PR #1653: feat: support dotenv (including ~/.codex/.env)
- URL: https://github.com/openai/codex/pull/1653
- Author: bolinfest
- Created: 2025-07-22 17:48:58 UTC
- Updated: 2025-07-22 22:54:41 UTC
- Changes: +23/-1, Files changed: 4, Commits: 1
Description
This PR adds a load_dotenv() helper function to the codex-common crate that is available when the cli feature is enabled. The function uses dotenvy to update the environment from:
$CODEX_HOME/.env$(pwd)/.env
To test:
- ran
printenv OPENAI_API_KEYto verify the env var exists in my environment - ran
just codex exec helloto verify the CLI uses myOPENAI_API_KEY - ran
unset OPENAI_API_KEY - ran
just codex exec helloagain and got ERROR: Missing environment variable:OPENAI_API_KEY, as expected - created
~/.codex/.envand addedOPENAI_API_KEY=sk-proj-...(also ranchmod 400 ~/.codex/.envfor good measure) - ran
just codex exec helloagain and it worked, verifying it picked upOPENAI_API_KEYfrom~/.codex/.env
Note this functionality was available in the TypeScript CLI: https://github.com/openai/codex/pull/122 and was recently requested over on https://github.com/openai/codex/issues/1262#issuecomment-3093203551.
Full Diff
diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock
index 9b4a4e32d4..3e4b84a435 100644
--- a/codex-rs/Cargo.lock
+++ b/codex-rs/Cargo.lock
@@ -756,7 +756,9 @@ version = "0.0.0"
dependencies = [
"anyhow",
"clap",
+ "codex-common",
"codex-core",
+ "dotenvy",
"landlock",
"libc",
"seccompiler",
@@ -1272,6 +1274,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+[[package]]
+name = "dotenvy"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+
[[package]]
name = "dupe"
version = "0.9.1"
diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs
index 8ed06c45af..2dfd3e55fe 100644
--- a/codex-rs/core/src/config.rs
+++ b/codex-rs/core/src/config.rs
@@ -561,7 +561,7 @@ fn default_model() -> String {
/// function will Err if the path does not exist.
/// - If `CODEX_HOME` is not set, this function does not verify that the
/// directory exists.
-fn find_codex_home() -> std::io::Result<PathBuf> {
+pub fn find_codex_home() -> std::io::Result<PathBuf> {
// Honor the `CODEX_HOME` environment variable when it is set to allow users
// (and tests) to override the default location.
if let Ok(val) = std::env::var("CODEX_HOME") {
diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml
index c8cd1078c0..5c2dea6083 100644
--- a/codex-rs/linux-sandbox/Cargo.toml
+++ b/codex-rs/linux-sandbox/Cargo.toml
@@ -17,7 +17,9 @@ workspace = true
[dependencies]
anyhow = "1"
clap = { version = "4", features = ["derive"] }
+codex-common = { path = "../common", features = ["cli"] }
codex-core = { path = "../core" }
+dotenvy = "0.15.7"
tokio = { version = "1", features = ["rt-multi-thread"] }
[dev-dependencies]
diff --git a/codex-rs/linux-sandbox/src/lib.rs b/codex-rs/linux-sandbox/src/lib.rs
index 568f015822..960678467c 100644
--- a/codex-rs/linux-sandbox/src/lib.rs
+++ b/codex-rs/linux-sandbox/src/lib.rs
@@ -43,6 +43,10 @@ where
crate::run_main();
}
+ // This modifies the environment, which is not thread-safe, so do this
+ // before creating any threads/the Tokio runtime.
+ load_dotenv();
+
// Regular invocation – create a Tokio runtime and execute the provided
// async entry-point.
let runtime = tokio::runtime::Runtime::new()?;
@@ -61,3 +65,11 @@ where
pub fn run_main() -> ! {
panic!("codex-linux-sandbox is only supported on Linux");
}
+
+/// Load env vars from ~/.codex/.env and `$(pwd)/.env`.
+fn load_dotenv() {
+ if let Ok(codex_home) = codex_core::config::find_codex_home() {
+ dotenvy::from_path(codex_home.join(".env")).ok();
+ }
+ dotenvy::dotenv().ok();
+}
Review Comments
codex-rs/common/src/dotenv.rs
- Created: 2025-07-22 21:54:08 UTC | Link: https://github.com/openai/codex/pull/1653#discussion_r2223902461
@@ -0,0 +1,7 @@
+/// Load env vars from ~/.codex/.env and `$(pwd)/.env`.
+pub fn load_dotenv() {
+ if let Ok(codex_home) = codex_core::config::find_codex_home() {
+ dotenvy::from_path(codex_home.join(".env")).ok();
+ }
+ dotenvy::dotenv().ok();
I think this could be argued either way, but I think there is a technical reason to do this as early as possible (before we know what the
Configeven is), which is that setting environment variables for the current process is not thread-safe, so it should really be done before any threads have been created.I just reworked this so that
load_dotenv()is now called before we set up the Tokio runtime.