fix(dotfiles): allow clearing aliases, disable import (#1995)

* fix(dotfiles): allow clearing aliases, disable import

At the moment there are far too many edge cases to handle importing
aliases.

1. We need an interactive shell to print aliases. Without it, most
   shells won't report much.
2. Many people have their shells print things on startup (graphics,
   fortunes, etc). This could be detected as an attempt to set an alias.

 Rather than spend the next year finding import edge cases, I'm
 disabling it for now. There's probably a better way we can do this?

* clippy
This commit is contained in:
Ellie Huxtable 2024-05-02 16:53:38 +01:00 committed by GitHub
parent 831dd783ed
commit 0639ff4960
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 39 additions and 24 deletions

1
Cargo.lock generated
View File

@ -207,6 +207,7 @@ dependencies = [
"itertools", "itertools",
"log", "log",
"ratatui", "ratatui",
"regex",
"rpassword", "rpassword",
"runtime-format", "runtime-format",
"rustix", "rustix",

View File

@ -9,7 +9,7 @@ exclude = ["ui/backend"]
[workspace.package] [workspace.package]
version = "18.2.0" version = "18.2.0"
authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"] authors = ["Ellie Huxtable <ellie@elliehuxtable.com>"]
rust-version = "1.67" rust-version = "1.70"
license = "MIT" license = "MIT"
homepage = "https://atuin.sh" homepage = "https://atuin.sh"
repository = "https://github.com/atuinsh/atuin" repository = "https://github.com/atuinsh/atuin"

View File

@ -37,7 +37,7 @@ async fn sync_download(
key: &Key, key: &Key,
force: bool, force: bool,
client: &api_client::Client<'_>, client: &api_client::Client<'_>,
db: &(impl Database + Send), db: &impl Database,
) -> Result<(i64, i64)> { ) -> Result<(i64, i64)> {
debug!("starting sync download"); debug!("starting sync download");
@ -127,7 +127,7 @@ async fn sync_upload(
key: &Key, key: &Key,
_force: bool, _force: bool,
client: &api_client::Client<'_>, client: &api_client::Client<'_>,
db: &(impl Database + Send), db: &impl Database,
) -> Result<()> { ) -> Result<()> {
debug!("starting sync upload"); debug!("starting sync upload");
@ -188,7 +188,7 @@ async fn sync_upload(
Ok(()) Ok(())
} }
pub async fn sync(settings: &Settings, force: bool, db: &(impl Database + Send)) -> Result<()> { pub async fn sync(settings: &Settings, force: bool, db: &impl Database) -> Result<()> {
let client = api_client::Client::new( let client = api_client::Client::new(
&settings.sync_address, &settings.sync_address,
&settings.session_token, &settings.session_token,

View File

@ -136,7 +136,7 @@ pub fn existing_aliases(shell: Option<Shell>) -> Result<Vec<Alias>, ShellError>
/// Import aliases from the current shell /// Import aliases from the current shell
/// This will not import aliases already in the store /// This will not import aliases already in the store
/// Returns aliases that were set /// Returns aliases that were set
pub async fn import_aliases(store: AliasStore) -> Result<Vec<Alias>> { pub async fn import_aliases(store: &AliasStore) -> Result<Vec<Alias>> {
let shell_aliases = existing_aliases(None)?; let shell_aliases = existing_aliases(None)?;
let store_aliases = store.aliases().await?; let store_aliases = store.aliases().await?;

View File

@ -82,6 +82,7 @@ uuid = { workspace = true }
unicode-segmentation = "1.11.0" unicode-segmentation = "1.11.0"
serde_yaml = "0.9.32" serde_yaml = "0.9.32"
sysinfo = "0.30.7" sysinfo = "0.30.7"
regex="1.10.4"
[target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies] [target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies]
cli-clipboard = { version = "0.4.0", optional = true } cli-clipboard = { version = "0.4.0", optional = true }

View File

@ -1,5 +1,5 @@
use clap::Subcommand; use clap::Subcommand;
use eyre::{Context, Result}; use eyre::{eyre, Context, Result};
use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings}; use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings};
@ -17,12 +17,19 @@ pub enum Cmd {
/// List all aliases /// List all aliases
List, List,
/// Import aliases set in the current shell /// Delete all aliases
Import, Clear,
// There are too many edge cases to parse at the moment. Disable for now.
// Import,
} }
impl Cmd { impl Cmd {
async fn set(&self, store: AliasStore, name: String, value: String) -> Result<()> { async fn set(&self, store: &AliasStore, name: String, value: String) -> Result<()> {
let illegal_char = regex::Regex::new("[ \t\n&();<>|\\\"'`$/]").unwrap();
if illegal_char.is_match(name.as_str()) {
return Err(eyre!("Illegal character in alias name"));
}
let aliases = store.aliases().await?; let aliases = store.aliases().await?;
let found: Vec<Alias> = aliases.into_iter().filter(|a| a.name == name).collect(); let found: Vec<Alias> = aliases.into_iter().filter(|a| a.name == name).collect();
@ -40,7 +47,7 @@ impl Cmd {
Ok(()) Ok(())
} }
async fn list(&self, store: AliasStore) -> Result<()> { async fn list(&self, store: &AliasStore) -> Result<()> {
let aliases = store.aliases().await?; let aliases = store.aliases().await?;
for i in aliases { for i in aliases {
@ -50,7 +57,17 @@ impl Cmd {
Ok(()) Ok(())
} }
async fn delete(&self, store: AliasStore, name: String) -> Result<()> { async fn clear(&self, store: &AliasStore) -> Result<()> {
let aliases = store.aliases().await?;
for i in aliases {
self.delete(store, i.name).await?;
}
Ok(())
}
async fn delete(&self, store: &AliasStore, name: String) -> Result<()> {
let mut aliases = store.aliases().await?.into_iter(); let mut aliases = store.aliases().await?.into_iter();
if let Some(alias) = aliases.find(|alias| alias.name == name) { if let Some(alias) = aliases.find(|alias| alias.name == name) {
println!("Deleting '{name}={}'.", alias.value); println!("Deleting '{name}={}'.", alias.value);
@ -61,7 +78,8 @@ impl Cmd {
Ok(()) Ok(())
} }
async fn import(&self, store: AliasStore) -> Result<()> { /*
async fn import(&self, store: &AliasStore) -> Result<()> {
let aliases = atuin_dotfiles::shell::import_aliases(store).await?; let aliases = atuin_dotfiles::shell::import_aliases(store).await?;
for i in aliases { for i in aliases {
@ -70,6 +88,7 @@ impl Cmd {
Ok(()) Ok(())
} }
*/
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> { pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
if !settings.dotfiles.enabled { if !settings.dotfiles.enabled {
@ -86,10 +105,10 @@ impl Cmd {
let alias_store = AliasStore::new(store, host_id, encryption_key); let alias_store = AliasStore::new(store, host_id, encryption_key);
match self { match self {
Self::Set { name, value } => self.set(alias_store, name.clone(), value.clone()).await, Self::Set { name, value } => self.set(&alias_store, name.clone(), value.clone()).await,
Self::Delete { name } => self.delete(alias_store, name.clone()).await, Self::Delete { name } => self.delete(&alias_store, name.clone()).await,
Self::List => self.list(alias_store).await, Self::List => self.list(&alias_store).await,
Self::Import => self.import(alias_store).await, Self::Clear => self.clear(&alias_store).await,
} }
} }
} }

View File

@ -308,13 +308,7 @@ impl State {
// handle keymap specific keybindings. // handle keymap specific keybindings.
match self.keymap_mode { match self.keymap_mode {
KeymapMode::VimNormal => match input.code { KeymapMode::VimNormal => match input.code {
KeyCode::Char('/') if !ctrl => { KeyCode::Char('?' | '/') if !ctrl => {
self.search.input.clear();
self.set_keymap_cursor(settings, "vim_insert");
self.keymap_mode = KeymapMode::VimInsert;
return InputAction::Continue;
}
KeyCode::Char('?') if !ctrl => {
self.search.input.clear(); self.search.input.clear();
self.set_keymap_cursor(settings, "vim_insert"); self.set_keymap_cursor(settings, "vim_insert");
self.keymap_mode = KeymapMode::VimInsert; self.keymap_mode = KeymapMode::VimInsert;

View File

@ -13,7 +13,7 @@ use atuin_history::stats::{compute, pretty_print};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(infer_subcommands = true)] #[command(infer_subcommands = true)]
pub struct Cmd { pub struct Cmd {
/// Compute statistics for the specified period, leave blank for statistics since the beginning. See https://docs.atuin.sh/reference/stats/ for more details. /// Compute statistics for the specified period, leave blank for statistics since the beginning. See [this](https://docs.atuin.sh/reference/stats/) for more details.
period: Vec<String>, period: Vec<String>,
/// How many top commands to list /// How many top commands to list