dotlock-bin 1.0.1

Encrypted project-local environment variables manager
//! `dl exec` (FG4): shell-form counterpart of `dl run`. The user-provided
//! command line is executed via `sh -c`, so `&&`, pipes and variable
//! expansion work. Secrets are injected exclusively as ENVIRONMENT variables
//! of the child shell — they are never interpolated into the command string,
//! so secret values cannot alter what gets executed. `dl run -- cmd args`
//! (argv form, no shell) remains the safe default; `dl exec` is the explicit
//! opt-in convenience for migration from `.env`-era scripts.

use crate::{
    cli::args::ExecArgs,
    commands::context::VaultContext,
    domain::model::DotLockResult,
    git::fetch::auto_fetch_if_enabled,
    runtime::{load_env_file_vars, run_with_secrets},
    storage::project::{ensure_project_initialized, vault_file},
};

pub fn run(args: ExecArgs) -> DotLockResult<()> {
    ensure_project_initialized()?;
    auto_fetch_if_enabled(&vault_file())?;
    let extra_env = match args.env_file.as_deref() {
        Some(path) => load_env_file_vars(path)?,
        None => Vec::new(),
    };
    let (metadata, dek) = VaultContext::unlock()?.into_read();
    // Join the (possibly unquoted) words back into one shell command line.
    // This is the user's OWN string — exactly what they would type after
    // `sh -c` themselves; no vault data ever becomes part of it.
    let command_line = args.command.join(" ");
    let command = vec!["sh".to_string(), "-c".to_string(), command_line];
    run_with_secrets(command, extra_env, &dek, &metadata)
}