eval: Add support for reading from a `.env` file (#29426)

Marshall Bowers created

This PR adds support for the eval to read environment variables from a
`.env` file located in the `crates/eval` directory.

For instance, you can use it to set your Anthropic API key:

```
ANTHROPIC_API_KEY=<secret>
```

Release Notes:

- N/A

Change summary

.gitignore              | 1 +
Cargo.lock              | 7 +++++++
Cargo.toml              | 1 +
crates/eval/Cargo.toml  | 1 +
crates/eval/README.md   | 2 ++
crates/eval/src/eval.rs | 7 ++++++-
6 files changed, 18 insertions(+), 1 deletion(-)

Detailed changes

.gitignore 🔗

@@ -32,4 +32,5 @@ Packages
 xcuserdata/
 
 # Don't commit any secrets to the repo.
+.env
 .env.secret.toml

Cargo.lock 🔗

@@ -4548,6 +4548,12 @@ dependencies = [
  "syn 2.0.100",
 ]
 
+[[package]]
+name = "dotenv"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
+
 [[package]]
 name = "dotenvy"
 version = "0.15.7"
@@ -4970,6 +4976,7 @@ dependencies = [
  "collections",
  "context_server",
  "dirs 5.0.1",
+ "dotenv",
  "env_logger 0.11.8",
  "extension",
  "fs",

Cargo.toml 🔗

@@ -437,6 +437,7 @@ dashmap = "6.0"
 dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "be69a016ba710191b9fdded28c8b042af4b617f7" }
 derive_more = "0.99.17"
 dirs = "4.0"
+dotenv = "0.15.0"
 ec4rs = "1.1"
 emojis = "0.6.1"
 env_logger = "0.11"

crates/eval/Cargo.toml 🔗

@@ -19,6 +19,7 @@ client.workspace = true
 collections.workspace = true
 context_server.workspace = true
 dirs = "5.0"
+dotenv.workspace = true
 env_logger.workspace = true
 extension.workspace = true
 fs.workspace = true

crates/eval/README.md 🔗

@@ -6,6 +6,8 @@ This eval assumes the working directory is the root of the repository. Run it wi
 cargo run -p eval
 ```
 
+The eval will optionally read a `.env` file in `crates/eval` if you need it to set environment variables, such as API keys.
+
 ## Explorer Tool
 
 The explorer tool generates a self-contained HTML view from one or more thread

crates/eval/src/eval.rs 🔗

@@ -34,9 +34,12 @@ use std::collections::VecDeque;
 use std::env;
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
-use std::sync::Arc;
+use std::sync::{Arc, LazyLock};
 use util::ResultExt as _;
 
+static CARGO_MANIFEST_DIR: LazyLock<PathBuf> =
+    LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")));
+
 #[derive(Parser, Debug)]
 #[command(name = "eval", disable_version_flag = true)]
 struct Args {
@@ -57,6 +60,8 @@ struct Args {
 }
 
 fn main() {
+    dotenv::from_filename(CARGO_MANIFEST_DIR.join(".env")).ok();
+
     env_logger::init();
 
     let system_id = ids::get_or_create_id(&ids::eval_system_id_path()).ok();