Skip to main content

auths_cli/factories/
mod.rs

1pub mod storage;
2
3use std::io::IsTerminal;
4use std::sync::Arc;
5
6use anyhow::Result;
7
8use auths_sdk::core_config::{EnvironmentConfig, load_config};
9use auths_sdk::keychain::{get_passphrase_cache, parse_duration_str};
10use auths_sdk::paths::auths_home;
11use auths_sdk::ports::agent::AgentSigningPort;
12use auths_sdk::signing::{KeychainPassphraseProvider, PassphraseProvider};
13use auths_telemetry::TelemetryShutdown;
14use auths_telemetry::config::{build_sinks_from_config, load_audit_config};
15use auths_telemetry::sinks::composite::CompositeSink;
16
17use crate::adapters::config_store::FileConfigStore;
18use crate::cli::AuthsCli;
19use crate::config::{CliConfig, OutputFormat};
20use crate::core::provider::{CliPassphraseProvider, PrefilledPassphraseProvider};
21
22/// Builds the full CLI configuration from parsed arguments.
23///
24/// Constructs the passphrase provider and output settings.
25/// This is the composition root — the only place where concrete adapter
26/// types are instantiated.
27///
28/// Args:
29/// * `cli`: The parsed CLI arguments.
30///
31/// Usage:
32/// ```ignore
33/// use auths_cli::factories::build_config;
34///
35/// let cli = AuthsCli::parse();
36/// let config = build_config(&cli)?;
37/// ```
38pub fn build_config(cli: &AuthsCli) -> Result<CliConfig> {
39    let output_format = if cli.json {
40        OutputFormat::Json
41    } else {
42        OutputFormat::Text
43    };
44
45    let env_config = EnvironmentConfig::from_env();
46
47    let passphrase_provider: Arc<dyn PassphraseProvider + Send + Sync> =
48        if let Some(passphrase) = env_config.keychain.passphrase.clone() {
49            Arc::new(PrefilledPassphraseProvider::new(zeroize::Zeroizing::new(
50                passphrase,
51            )))
52        } else {
53            let config = load_config(&FileConfigStore);
54            let cache = get_passphrase_cache(config.passphrase.biometric);
55            let ttl_secs = config
56                .passphrase
57                .duration
58                .as_deref()
59                .and_then(parse_duration_str);
60            let inner = Arc::new(CliPassphraseProvider::new());
61            Arc::new(KeychainPassphraseProvider::new(
62                inner,
63                cache,
64                "default".to_string(),
65                config.passphrase.cache,
66                ttl_secs,
67            ))
68        };
69
70    let is_interactive = std::io::stdout().is_terminal();
71
72    // Honor the global --repo flag, falling back to AUTHS_REPO so a headless CI step
73    // (which exports AUTHS_REPO from `init --profile ci`) finds the same registry.
74    let repo_path = cli.repo.clone().or_else(|| {
75        std::env::var("AUTHS_REPO")
76            .ok()
77            .filter(|s| !s.is_empty())
78            .map(std::path::PathBuf::from)
79    });
80
81    Ok(CliConfig {
82        repo_path,
83        output_format,
84        is_interactive,
85        passphrase_provider,
86        env_config,
87    })
88}
89
90/// Loads audit sinks from `~/.auths/audit.toml` and initialises the global
91/// telemetry pipeline.
92///
93/// Returns `None` when no sinks are configured — zero overhead in that case.
94///
95/// Usage:
96/// ```ignore
97/// let _telemetry = auths_cli::factories::init_audit_sinks();
98/// ```
99pub fn init_audit_sinks() -> Option<TelemetryShutdown> {
100    let audit_path = match auths_home() {
101        Ok(h) => h.join("audit.toml"),
102        Err(_) => return None,
103    };
104    let config = load_audit_config(&audit_path);
105    #[allow(clippy::disallowed_methods)] // CLI boundary: audit config reads env vars
106    let sinks = build_sinks_from_config(&config, |name| std::env::var(name).ok());
107    if sinks.is_empty() {
108        return None;
109    }
110    let composite = Arc::new(CompositeSink::new(sinks));
111    Some(auths_telemetry::init_telemetry_with_sink(composite))
112}
113
114/// Build the platform-appropriate agent signing provider.
115///
116/// Returns `CliAgentAdapter` on Unix, `NoopAgentProvider` elsewhere.
117///
118/// Usage:
119/// ```ignore
120/// let agent = build_agent_provider();
121/// let ctx = CommitSigningContext { agent_signing: agent, .. };
122/// ```
123pub fn build_agent_provider() -> Arc<dyn AgentSigningPort + Send + Sync> {
124    #[cfg(unix)]
125    {
126        Arc::new(crate::adapters::agent::CliAgentAdapter)
127    }
128    #[cfg(not(unix))]
129    {
130        Arc::new(auths_sdk::ports::agent::NoopAgentProvider)
131    }
132}