Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Entry point. Argument parsing and top-level command dispatch live in
//! `commands::Cli`.

use std::path::{Path, PathBuf};
use std::process::ExitCode;

use clap::Parser;
Expand All @@ -13,10 +14,7 @@ mod credentials;
mod http;

fn main() -> ExitCode {
// Load .env from CWD if present. Process env vars still take precedence
// over .env, so users can override per-invocation. Silently ignored
// when no .env exists (the common case for installed users).
let _ = dotenvy::dotenv();
load_dotenv();

let cli = commands::Cli::parse();
match cli.run() {
Expand All @@ -27,3 +25,34 @@ fn main() -> ExitCode {
}
}
}

/// Walk from cwd up to the filesystem root looking for the first `.env` and
/// load it. Process env vars still win over `.env` because dotenvy's default
/// is non-overriding. Silently ignored when no `.env` exists anywhere on the
/// path (the common case for installed users).
///
/// Why upward search and not just `dotenvy::dotenv()`: developers run
/// `mirrorstack` from many places inside their workspace — meta-repo root,
/// a module subdirectory, a scaffold target. The cwd-only behavior surprised
/// users by silently missing the workspace `.env` that lived one or two
/// dirs up.
fn load_dotenv() {
if let Some(path) = find_dotenv_upward() {
let _ = dotenvy::from_path(&path);
}
}

fn find_dotenv_upward() -> Option<PathBuf> {
let cwd = std::env::current_dir().ok()?;
let mut dir: &Path = cwd.as_path();
loop {
let candidate = dir.join(".env");
if candidate.is_file() {
return Some(candidate);
}
match dir.parent() {
Some(parent) => dir = parent,
None => return None,
}
}
}
Loading