Skip to content

Commit 91e2230

Browse files
authored
[ENH] CLI - Set Chroma env variables (#4711)
## Description of changes Adding `chroma db connect --env-file` which sets environment variables required for connecting to ChromaCloud in the `.env` file in the current directory (or create it). The `chroma db connect --env-vars` option will output to the terminal. ## Test plan Manual testing. ## Documentation Changes Included in this PR under CLI docs.
1 parent c4d0c93 commit 91e2230

File tree

3 files changed

+95
-3
lines changed
  • docs/docs.trychroma.com/markdoc/content/cli/commands
  • rust/cli/src

3 files changed

+95
-3
lines changed

docs/docs.trychroma.com/markdoc/content/cli/commands/db.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ The output code snippet will already have the API key of your profile set for th
1212
chroma db connect [db_name] [--language python/JS/TS]
1313
```
1414

15+
The `connect` command can also add Chroma environment variables (`CHROMA_API_KEY`, `CHROMA_TENANT`, and `CHROMA_DATABASE`) to a `.env` file in your current working directory. It will create a `.env` file for you if it doesn't exist:
16+
17+
```terminal
18+
chroma db connect [db_name] --env-file
19+
```
20+
21+
If you prefer to simply output these variables to your terminal use:
22+
23+
```terminal
24+
chroma db connect [db_name] --env-vars
25+
```
26+
27+
Setting these environment variables will allow you to concisely instantiate the `CloudClient` with no arguments.
28+
1529
### Create
1630

1731
The `create` command lets you create a database on Chroma Cloud. It has the `name` argument, which is the name of the DB you want to create. If you don't provide it, the CLI will prompt you to choose a name.

rust/cli/src/commands/db.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
use crate::client::admin_client::get_admin_client;
22
use crate::ui_utils::copy_to_clipboard;
3-
use crate::utils::{get_current_profile, CliError, Profile, UtilsError, SELECTION_LIMIT};
3+
use crate::utils::{
4+
get_current_profile, CliError, Profile, UtilsError, CHROMA_API_KEY_ENV_VAR,
5+
CHROMA_DATABASE_ENV_VAR, CHROMA_TENANT_ENV_VAR, SELECTION_LIMIT,
6+
};
47
use chroma_types::Database;
58
use clap::{Args, Subcommand, ValueEnum};
69
use colored::Colorize;
710
use dialoguer::theme::ColorfulTheme;
811
use dialoguer::{Input, Select};
9-
use std::fmt;
12+
use std::error::Error;
13+
use std::path::Path;
14+
use std::{fmt, fs};
1015
use strum::IntoEnumIterator;
1116
use strum_macros::EnumIter;
1217
use thiserror::Error;
1318

1419
#[derive(Debug, Error)]
1520
pub enum DbError {
16-
#[error("")]
21+
#[error("No databases found")]
1722
NoDBs,
1823
#[error("Database name cannot be empty")]
1924
EmptyDbName,
@@ -23,6 +28,8 @@ pub enum DbError {
2328
DbNotFound(String),
2429
#[error("Failed to get runtime for DB commands")]
2530
RuntimeError,
31+
#[error("Failed to create or update .env file with Chroma environment variables")]
32+
EnvFile,
2633
}
2734

2835
#[derive(Debug, Clone, ValueEnum, EnumIter)]
@@ -98,6 +105,10 @@ pub struct ConnectArgs {
98105
help = "The programming language to use for the connection snippet"
99106
)]
100107
language: Option<Language>,
108+
#[clap(long = "env-file", default_value_t = false, conflicts_with_all = ["language", "env_vars"], help = "Add Chroma environment variables to a .env file in the current directory")]
109+
env_file: bool,
110+
#[clap(long = "env-vars", default_value_t = false, conflicts_with_all = ["language", "env_file"], help = "Output Chroma environment variables")]
111+
env_vars: bool,
101112
}
102113

103114
#[derive(Args, Debug)]
@@ -147,6 +158,13 @@ fn no_dbs_message(profile_name: &str) -> String {
147158
)
148159
}
149160

161+
fn env_file_created_message() -> String {
162+
format!(
163+
"{}",
164+
"Chroma environment variables set in .env!".blue().bold()
165+
)
166+
}
167+
150168
fn select_language_message() -> String {
151169
"Which language would you like to use?".to_string()
152170
}
@@ -318,6 +336,48 @@ fn confirm_db_deletion(name: &str) -> Result<bool, CliError> {
318336
Ok(confirm.eq(name))
319337
}
320338

339+
fn create_env_connection(current_profile: Profile, db_name: String) -> Result<(), Box<dyn Error>> {
340+
let env_path = ".env";
341+
let chroma_keys = [
342+
CHROMA_API_KEY_ENV_VAR,
343+
CHROMA_TENANT_ENV_VAR,
344+
CHROMA_DATABASE_ENV_VAR,
345+
];
346+
347+
let mut lines = Vec::new();
348+
349+
if Path::new(env_path).exists() {
350+
let content = fs::read_to_string(env_path)?;
351+
352+
for line in content.lines() {
353+
let keep = if let Some(eq_pos) = line.find('=') {
354+
let key = line[..eq_pos].trim();
355+
!chroma_keys.contains(&key)
356+
} else {
357+
true
358+
};
359+
360+
if keep {
361+
lines.push(line.to_string());
362+
}
363+
}
364+
}
365+
366+
lines.push(format!(
367+
"{}={}",
368+
CHROMA_API_KEY_ENV_VAR, current_profile.api_key
369+
));
370+
lines.push(format!(
371+
"{}={}",
372+
CHROMA_TENANT_ENV_VAR, current_profile.tenant_id
373+
));
374+
lines.push(format!("{}={}", CHROMA_DATABASE_ENV_VAR, db_name));
375+
376+
fs::write(env_path, lines.join("\n") + "\n")?;
377+
378+
Ok(())
379+
}
380+
321381
pub async fn connect(args: ConnectArgs, current_profile: Profile) -> Result<(), CliError> {
322382
let admin_client = get_admin_client(Some(&current_profile), args.db_args.dev);
323383
let dbs = admin_client.list_databases().await?;
@@ -331,6 +391,21 @@ pub async fn connect(args: ConnectArgs, current_profile: Profile) -> Result<(),
331391
return Err(CliError::Db(DbError::DbNotFound(name)));
332392
}
333393

394+
if args.env_file {
395+
if create_env_connection(current_profile, name).is_err() {
396+
return Err(DbError::EnvFile.into());
397+
}
398+
println!("{}", env_file_created_message());
399+
return Ok(());
400+
}
401+
402+
if args.env_vars {
403+
println!("{}={}", CHROMA_API_KEY_ENV_VAR, current_profile.api_key);
404+
println!("{}={}", CHROMA_TENANT_ENV_VAR, current_profile.tenant_id);
405+
println!("{}={}", CHROMA_DATABASE_ENV_VAR, name);
406+
return Ok(());
407+
}
408+
334409
let language = match args.language {
335410
Some(language) => language,
336411
None => select_language()?,

rust/cli/src/utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ pub const CHROMA_DIR: &str = ".chroma";
3434
pub const CREDENTIALS_FILE: &str = "credentials";
3535
const CONFIG_FILE: &str = "config.json";
3636
pub const SELECTION_LIMIT: usize = 5;
37+
pub const CHROMA_API_KEY_ENV_VAR: &str = "CHROMA_API_KEY";
38+
pub const CHROMA_TENANT_ENV_VAR: &str = "CHROMA_TENANT";
39+
pub const CHROMA_DATABASE_ENV_VAR: &str = "CHROMA_DATABASE";
3740

3841
#[derive(Debug, Error)]
3942
pub enum CliError {

0 commit comments

Comments
 (0)