Skip to main content

auths_cli/commands/
reset.rs

1use anyhow::{Context, Result};
2use clap::Parser;
3
4use crate::adapters::git_config::SystemGitConfigProvider;
5use crate::commands::executable::ExecutableCommand;
6use crate::config::CliConfig;
7use crate::ux::format::Output;
8use auths_sdk::ports::git_config::GitConfigProvider;
9
10/// Git config keys that `auths init` sets for signing.
11const GIT_SIGNING_CONFIG_KEYS: &[&str] = &[
12    "gpg.format",
13    "gpg.ssh.program",
14    "user.signingkey",
15    "commit.gpgsign",
16    "tag.gpgsign",
17    "gpg.ssh.allowedSignersFile",
18];
19
20/// Remove Auths identity and git configuration, allowing a clean re-initialization.
21///
22/// This is a destructive operation that removes your local identity
23/// and signing configuration. Your identity can be recovered from
24/// a paired device if you have one.
25///
26/// Usage:
27/// ```ignore
28/// auths reset
29/// auths reset --force  # skip confirmation
30/// ```
31#[derive(Parser, Debug, Clone)]
32#[command(
33    name = "reset",
34    about = "Remove Auths identity and git signing configuration"
35)]
36pub struct ResetCommand {
37    /// Skip confirmation prompt
38    #[arg(long)]
39    force: bool,
40}
41
42impl ExecutableCommand for ResetCommand {
43    fn execute(&self, ctx: &CliConfig) -> Result<()> {
44        let out = Output::new();
45
46        if !self.force {
47            let confirmed = dialoguer::Confirm::new()
48                .with_prompt("This will remove ~/.auths and git signing config. Continue? [y/N]")
49                .default(false)
50                .interact()
51                .context("Failed to read confirmation")?;
52
53            if !confirmed {
54                out.println("Aborted.");
55                return Ok(());
56            }
57        }
58
59        out.newline();
60        out.print_heading("Resetting Auths...");
61        out.newline();
62
63        remove_auths_directory(&out)?;
64        clear_keychain_keys(&out, &ctx.env_config);
65        unset_git_signing_config(&out)?;
66
67        out.newline();
68        out.print_success("Reset complete. Run 'auths init' to set up again.");
69
70        Ok(())
71    }
72}
73
74fn remove_auths_directory(out: &Output) -> Result<()> {
75    let auths_dir = dirs::home_dir()
76        .ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?
77        .join(".auths");
78
79    if auths_dir.exists() {
80        std::fs::remove_dir_all(&auths_dir)
81            .with_context(|| format!("Failed to remove {}", auths_dir.display()))?;
82        out.print_success(&format!("Removed {}", auths_dir.display()));
83    } else {
84        out.print_info(&format!("{} does not exist, skipping", auths_dir.display()));
85    }
86
87    Ok(())
88}
89
90fn unset_git_signing_config(out: &Output) -> Result<()> {
91    let provider = SystemGitConfigProvider::global();
92
93    for key in GIT_SIGNING_CONFIG_KEYS {
94        match provider.unset(key) {
95            Ok(()) => out.print_success(&format!("Unset git config {key}")),
96            Err(e) => out.print_warn(&format!("Could not unset {key}: {e}")),
97        }
98    }
99
100    Ok(())
101}
102
103/// Clear all keys from the keychain backend.
104fn clear_keychain_keys(out: &Output, env_config: &auths_sdk::core_config::EnvironmentConfig) {
105    let keychain = match auths_sdk::keychain::get_platform_keychain_with_config(env_config) {
106        Ok(k) => k,
107        Err(e) => {
108            out.print_warn(&format!("Could not access keychain to clear keys: {e}"));
109            return;
110        }
111    };
112
113    let aliases = match keychain.list_aliases() {
114        Ok(a) => a,
115        Err(e) => {
116            out.print_warn(&format!("Could not list keychain keys: {e}"));
117            return;
118        }
119    };
120
121    if aliases.is_empty() {
122        return;
123    }
124
125    for alias in &aliases {
126        match keychain.delete_key(alias) {
127            Ok(()) => {}
128            Err(e) => {
129                out.print_warn(&format!("Could not delete key '{}': {e}", alias.as_str()));
130            }
131        }
132    }
133
134    out.print_success(&format!(
135        "Cleared {} key(s) from {} keychain",
136        aliases.len(),
137        keychain.backend_name()
138    ));
139}