use std::fs;
use std::io::{BufRead, BufReader};
use std::path::Path;
use dialoguer::Confirm;
use crate::cli::env_parser::parse_env_line;
use crate::cli::output;
use crate::cli::{load_keyfile, prompt_new_password, Cli};
use crate::config::Settings;
use crate::errors::{EnvVaultError, Result};
use crate::vault::VaultStore;
pub fn execute(cli: &Cli) -> Result<()> {
let cwd = std::env::current_dir()?;
let vault_dir = cwd.join(&cli.vault_dir);
let env = &cli.env;
let vault_path = vault_dir.join(format!("{env}.vault"));
if !vault_dir.exists() {
fs::create_dir_all(&vault_dir)?;
let dir_display = vault_dir.display();
output::info(&format!("Created vault directory: {dir_display}"));
}
if vault_path.exists() {
output::tip("Use `envvault set` to add secrets to the existing vault.");
return Err(EnvVaultError::VaultAlreadyExists(vault_path));
}
let password = prompt_new_password()?;
let keyfile = load_keyfile(cli)?;
let settings = Settings::load(&cwd)?;
let mut store = VaultStore::create(
&vault_path,
password.as_bytes(),
&cli.env,
Some(&settings.argon2_params()),
keyfile.as_deref(),
)?;
if keyfile.is_some() {
output::info("Vault created with keyfile — you must pass --keyfile on every command.");
}
output::success(&format!(
"Vault created for '{}' environment at {}",
cli.env,
vault_path.display()
));
let env_file = cwd.join(".env");
if env_file.exists() {
let should_import = Confirm::new()
.with_prompt("Found .env file. Import secrets from it?")
.default(true)
.interact()
.map_err(|e| {
EnvVaultError::CommandFailed(format!("failed to read confirmation: {e}"))
})?;
if should_import {
let count = import_env_file(&env_file, &mut store)?;
store.save()?;
output::success(&format!("Imported {count} secrets from .env"));
}
}
crate::cli::gitignore::patch_gitignore(&cwd, &format!("{}/", cli.vault_dir));
match crate::git::install_hook(&cwd) {
Ok(crate::git::InstallResult::Installed) => {
output::info("Installed pre-commit hook to detect secret leaks.");
}
Ok(crate::git::InstallResult::ExistingHookFound) => {
output::warning("A pre-commit hook already exists — EnvVault hook was not installed.");
}
Ok(
crate::git::InstallResult::AlreadyInstalled | crate::git::InstallResult::NotAGitRepo,
)
| Err(_) => {} }
crate::audit::log_audit(cli, "init", None, Some("vault created"));
output::tip("Run `envvault set <KEY>` to add a secret.");
output::tip("Run `envvault list` to see all secrets.");
output::tip("Run `envvault run -- <command>` to inject secrets into a command.");
Ok(())
}
fn import_env_file(path: &Path, store: &mut VaultStore) -> Result<usize> {
let file = fs::File::open(path)?;
let reader = BufReader::new(file);
let mut count = 0;
for line in reader.lines() {
let line = line?;
if let Some((key, value)) = parse_env_line(&line) {
store.set_secret(key, value)?;
count += 1;
}
}
Ok(count)
}