mirror of
https://github.com/openai/codex.git
synced 2026-04-28 18:32:04 +03:00
131 lines
4.9 KiB
Markdown
131 lines
4.9 KiB
Markdown
# 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. |