fix(macros): don't mutate environment variables (#3848)

This commit is contained in:
Joey de Waal 2025-05-19 23:58:26 +02:00 committed by GitHub
parent 4259862adf
commit 760b3953ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::{fs, io};
@ -74,6 +74,7 @@ struct Metadata {
manifest_dir: PathBuf,
offline: bool,
database_url: Option<String>,
offline_dir: Option<String>,
workspace_root: Arc<Mutex<Option<PathBuf>>>,
}
@ -114,39 +115,21 @@ static METADATA: Lazy<Mutex<HashMap<String, Metadata>>> = Lazy::new(Default::def
fn init_metadata(manifest_dir: &String) -> Metadata {
let manifest_dir: PathBuf = manifest_dir.into();
// If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this,
// otherwise fallback to default dotenv behaviour.
let env_path = manifest_dir.join(".env");
#[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))]
let env_path = if env_path.exists() {
// Load the new environment variables and override the old ones if necessary.
let res = dotenvy::from_path_override(&env_path);
if let Err(e) = res {
panic!("failed to load environment from {env_path:?}, {e}");
}
Some(env_path)
} else {
dotenvy::dotenv_override().ok()
};
// tell the compiler to watch the `.env` for changes, if applicable
#[cfg(procmacro2_semver_exempt)]
if let Some(env_path) = env_path.as_ref().and_then(|path| path.to_str()) {
proc_macro::tracked_path::path(env_path);
}
let (database_url, offline, offline_dir) = load_dot_env(&manifest_dir);
let offline = env("SQLX_OFFLINE")
.ok()
.or(offline)
.map(|s| s.eq_ignore_ascii_case("true") || s == "1")
.unwrap_or(false);
let database_url = env("DATABASE_URL").ok();
let database_url = env("DATABASE_URL").ok().or(database_url);
Metadata {
manifest_dir,
offline,
database_url,
offline_dir,
workspace_root: Arc::new(Mutex::new(None)),
}
}
@ -182,7 +165,7 @@ pub fn expand_input<'a>(
// Check SQLX_OFFLINE_DIR, then local .sqlx, then workspace .sqlx.
let dirs = [
|_: &Metadata| env("SQLX_OFFLINE_DIR").ok().map(PathBuf::from),
|meta: &Metadata| meta.offline_dir.as_deref().map(PathBuf::from),
|meta: &Metadata| Some(meta.manifest_dir.join(".sqlx")),
|meta: &Metadata| Some(meta.workspace_root().join(".sqlx")),
];
@ -402,3 +385,52 @@ fn env(name: &str) -> Result<String, std::env::VarError> {
std::env::var(name)
}
}
/// Get `DATABASE_URL`, `SQLX_OFFLINE` and `SQLX_OFFLINE_DIR` from the `.env`.
fn load_dot_env(manifest_dir: &Path) -> (Option<String>, Option<String>, Option<String>) {
let mut env_path = manifest_dir.join(".env");
// If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this,
// otherwise fallback to default dotenv file.
#[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))]
let env_file = if env_path.exists() {
let res = dotenvy::from_path_iter(&env_path);
match res {
Ok(iter) => Some(iter),
Err(e) => panic!("failed to load environment from {env_path:?}, {e}"),
}
} else {
#[allow(unused_assignments)]
{
env_path = PathBuf::from(".env");
}
dotenvy::dotenv_iter().ok()
};
let mut offline = None;
let mut database_url = None;
let mut offline_dir = None;
if let Some(env_file) = env_file {
// tell the compiler to watch the `.env` for changes.
#[cfg(procmacro2_semver_exempt)]
if let Some(env_path) = env_path.to_str() {
proc_macro::tracked_path::path(env_path);
}
for item in env_file {
let Ok((key, value)) = item else {
continue;
};
match key.as_str() {
"DATABASE_URL" => database_url = Some(value),
"SQLX_OFFLINE" => offline = Some(value),
"SQLX_OFFLINE_DIR" => offline_dir = Some(value),
_ => {}
};
}
}
(database_url, offline, offline_dir)
}