Files
codex/prs/bolinfest/PR-1653.md
2025-09-02 15:17:45 -07:00

131 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](https://crates.io/crates/dotenvy) to update the environment from:
- `$CODEX_HOME/.env`
- `$(pwd)/.env`
To test:
- ran `printenv OPENAI_API_KEY` to verify the env var exists in my environment
- ran `just codex exec hello` to verify the CLI uses my `OPENAI_API_KEY`
- ran `unset OPENAI_API_KEY`
- ran `just codex exec hello` again and got **ERROR: Missing environment variable: `OPENAI_API_KEY`**, as expected
- created `~/.codex/.env` and added `OPENAI_API_KEY=sk-proj-...` (also ran `chmod 400 ~/.codex/.env` for good measure)
- ran `just codex exec hello` again and it worked, verifying it picked up `OPENAI_API_KEY` from `~/.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
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
```diff
@@ -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 `Config` even 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.