pub mod storage;
use std::io::IsTerminal;
use std::sync::Arc;
use anyhow::Result;
use auths_sdk::core_config::{EnvironmentConfig, load_config};
use auths_sdk::keychain::{get_passphrase_cache, parse_duration_str};
use auths_sdk::paths::auths_home;
use auths_sdk::ports::agent::AgentSigningPort;
use auths_sdk::signing::{KeychainPassphraseProvider, PassphraseProvider};
use auths_telemetry::TelemetryShutdown;
use auths_telemetry::config::{build_sinks_from_config, load_audit_config};
use auths_telemetry::sinks::composite::CompositeSink;
use crate::adapters::config_store::FileConfigStore;
use crate::cli::AuthsCli;
use crate::config::{CliConfig, OutputFormat};
use crate::core::provider::{CliPassphraseProvider, PrefilledPassphraseProvider};
pub fn build_config(cli: &AuthsCli) -> Result<CliConfig> {
let output_format = if cli.json {
OutputFormat::Json
} else {
OutputFormat::Text
};
let env_config = EnvironmentConfig::from_env();
let passphrase_provider: Arc<dyn PassphraseProvider + Send + Sync> =
if let Some(passphrase) = env_config.keychain.passphrase.clone() {
Arc::new(PrefilledPassphraseProvider::new(zeroize::Zeroizing::new(
passphrase,
)))
} else {
let config = load_config(&FileConfigStore);
let cache = get_passphrase_cache(config.passphrase.biometric);
let ttl_secs = config
.passphrase
.duration
.as_deref()
.and_then(parse_duration_str);
let inner = Arc::new(CliPassphraseProvider::new());
Arc::new(KeychainPassphraseProvider::new(
inner,
cache,
"default".to_string(),
config.passphrase.cache,
ttl_secs,
))
};
let is_interactive = std::io::stdout().is_terminal();
let repo_path = resolve_repo_root(cli.repo.clone());
Ok(CliConfig {
repo_path,
output_format,
is_interactive,
passphrase_provider,
env_config,
})
}
fn resolve_repo_root(repo_flag: Option<std::path::PathBuf>) -> Option<std::path::PathBuf> {
#[allow(clippy::disallowed_methods)] let auths_repo = std::env::var("AUTHS_REPO").ok().filter(|s| !s.is_empty());
#[allow(clippy::disallowed_methods)] let auths_home = std::env::var("AUTHS_HOME").ok().filter(|s| !s.is_empty());
let (root, via_home_alias) = pick_repo_root(repo_flag, auths_repo, auths_home);
if via_home_alias {
eprintln!(
"warning: AUTHS_HOME is deprecated; prefer AUTHS_REPO to override the storage root"
);
}
root
}
fn pick_repo_root(
repo_flag: Option<std::path::PathBuf>,
auths_repo: Option<String>,
auths_home: Option<String>,
) -> (Option<std::path::PathBuf>, bool) {
if let Some(flag) = repo_flag {
return (Some(flag), false);
}
if let Some(repo) = auths_repo {
return (Some(std::path::PathBuf::from(repo)), false);
}
match auths_home {
Some(home) => (Some(std::path::PathBuf::from(home)), true),
None => (None, false),
}
}
pub fn init_audit_sinks() -> Option<TelemetryShutdown> {
let audit_path = match auths_home() {
Ok(h) => h.join("audit.toml"),
Err(_) => return None,
};
let config = load_audit_config(&audit_path);
#[allow(clippy::disallowed_methods)] let sinks = build_sinks_from_config(&config, |name| std::env::var(name).ok());
if sinks.is_empty() {
return None;
}
let composite = Arc::new(CompositeSink::new(sinks));
Some(auths_telemetry::init_telemetry_with_sink(composite))
}
pub fn build_agent_provider() -> Arc<dyn AgentSigningPort + Send + Sync> {
#[cfg(unix)]
{
Arc::new(crate::adapters::agent::CliAgentAdapter)
}
#[cfg(not(unix))]
{
Arc::new(auths_sdk::ports::agent::NoopAgentProvider)
}
}
#[cfg(test)]
mod tests {
use super::pick_repo_root;
use std::path::PathBuf;
#[test]
fn repo_flag_wins_over_both_env_vars() {
let (root, via_home) = pick_repo_root(
Some(PathBuf::from("/flag")),
Some("/repo".into()),
Some("/home".into()),
);
assert_eq!(root, Some(PathBuf::from("/flag")));
assert!(!via_home);
}
#[test]
fn auths_repo_wins_over_auths_home() {
let (root, via_home) = pick_repo_root(None, Some("/repo".into()), Some("/home".into()));
assert_eq!(root, Some(PathBuf::from("/repo")));
assert!(!via_home);
}
#[test]
fn auths_home_populates_repo_path_with_warning() {
let (root, via_home) = pick_repo_root(None, None, Some("/home".into()));
assert_eq!(root, Some(PathBuf::from("/home")));
assert!(via_home, "AUTHS_HOME must trigger the deprecation warning");
}
#[test]
fn no_override_leaves_default_root() {
let (root, via_home) = pick_repo_root(None, None, None);
assert_eq!(root, None);
assert!(!via_home);
}
}