mirror of
https://github.com/atuinsh/atuin.git
synced 2026-10-12 02:11:09 +00:00
feat: config disable (atuinsh/atuin#4349)
I added an `atuin config disable` command because my update script looks something like this:
```powershell
if (Get-Command "atuin" -ErrorAction SilentlyContinue) {
atuin config set daemon.enabled false
atuin config set daemon.autostart false
atuin config set search_mode fuzzy
atuin daemon stop
}
cargo install --locked atuin
if (Get-Command "atuin" -ErrorAction SilentlyContinue) {
atuin config enable daemon
}
```
This is necessary on Windows, as it doesn't allow you to overwrite a running executable, and I wanted to be able to use another console during the update.
Also, I figured if there's an `enable` command, there should probably also be a `disable` one 🙂
The second commit is unrelated, it just fixes a few build warnings on Windows.
## Checks
- [x] I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle
- [x] I have checked that there are no existing pull requests for the same thing
This commit is contained in:
committed by
Ellie Huxtable
parent
d95cd5146b
commit
9740b0a422
@@ -357,6 +357,7 @@ mod tests {
|
||||
OpenOptions::new().append(true).open(path).unwrap().write_all(bytes).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn replace(path: &Path, contents: &[u8]) {
|
||||
let tmp = path.with_file_name("tmp");
|
||||
std::fs::write(&tmp, contents).unwrap();
|
||||
|
||||
@@ -308,6 +308,7 @@ pub fn try_read_pid(pidfile_path: &Path) -> Option<u32> {
|
||||
mod tests {
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
#[cfg(unix)]
|
||||
use proptest::prelude::*;
|
||||
use rstest::{fixture, rstest};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use atuin_client::settings::Settings;
|
||||
use eyre::{Context, Result};
|
||||
#[cfg(unix)]
|
||||
use eyre::Context;
|
||||
use eyre::Result;
|
||||
|
||||
use crate::components::search::SearchGrpcService;
|
||||
use crate::daemon::DaemonHandle;
|
||||
|
||||
@@ -118,6 +118,7 @@ arg-totp-code = The two-factor authentication code for your account, if any
|
||||
cmd-config-get = Get a configuration value from your config.toml file or after defaults and overrides are applied
|
||||
cmd-config-set = Set a configuration value in your config.toml file
|
||||
cmd-config-enable = Enable a feature, along with everything it depends on
|
||||
cmd-config-disable = Disable a feature, leaving its dependencies enabled
|
||||
cmd-config-print = Print all configuration values from your config.toml file in TOML format
|
||||
.long =
|
||||
Print all configuration values from your config.toml file in TOML format
|
||||
@@ -135,9 +136,10 @@ value-config-set-the-type-boolean = Store value as a boolean
|
||||
value-config-set-the-type-integer = Store value as an integer
|
||||
value-config-set-the-type-float = Store the value as a float
|
||||
arg-config-enable-feature = The feature to enable
|
||||
value-config-enable-feature-daemon = Run the daemon, autostart it, and use it for search
|
||||
value-config-enable-feature-output-capture = Capture command output, via the daemon and the pty proxy
|
||||
value-config-enable-feature-octavo = Use Octavo, once it is enabled for your account on the hub
|
||||
arg-config-disable-feature = The feature to disable
|
||||
value-config-feature-daemon = The background daemon and daemon-backed search
|
||||
value-config-feature-output-capture = Command output capture, via the daemon and the pty proxy
|
||||
value-config-feature-octavo = Use Octavo, once it is enabled for your account on the hub
|
||||
config-enable-octavo-not-logged-in = You are not logged in to the hub, so it cannot say whether Octavo is enabled for your account. Run 'atuin login' first.
|
||||
config-enable-octavo-not-enabled = Octavo is not enabled for your account on { $address }.
|
||||
config-enable-octavo-description = Octavo is Atuin's sync for sharing and searching your history.
|
||||
|
||||
@@ -32,6 +32,9 @@ pub enum Cmd {
|
||||
#[command(about = fl!("cmd-config-enable"))]
|
||||
Enable(EnableCmd),
|
||||
|
||||
#[command(about = fl!("cmd-config-disable"))]
|
||||
Disable(DisableCmd),
|
||||
|
||||
#[command(about = fl!("cmd-config-print"), long_about = fl!("cmd-config-print", "long"))]
|
||||
Print(PrintCmd),
|
||||
}
|
||||
@@ -43,6 +46,7 @@ impl Cmd {
|
||||
Self::Get(get) => get.run(settings).await,
|
||||
Self::Set(set) => set.run(settings).await,
|
||||
Self::Enable(enable) => enable.run(settings).await,
|
||||
Self::Disable(disable) => disable.run(settings).await,
|
||||
Self::Print(print) => print.run(settings).await,
|
||||
}
|
||||
}
|
||||
@@ -245,14 +249,20 @@ pub struct EnableCmd {
|
||||
pub feature: Feature,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct DisableCmd {
|
||||
#[arg(value_enum, help = fl!("arg-config-disable-feature"))]
|
||||
pub feature: Feature,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Feature {
|
||||
#[value(help = fl!("value-config-enable-feature-daemon"))]
|
||||
#[value(help = fl!("value-config-feature-daemon"))]
|
||||
Daemon,
|
||||
#[value(help = fl!("value-config-enable-feature-output-capture"))]
|
||||
#[value(help = fl!("value-config-feature-output-capture"))]
|
||||
OutputCapture,
|
||||
#[cfg(feature = "octavo")]
|
||||
#[value(help = fl!("value-config-enable-feature-octavo"))]
|
||||
#[value(help = fl!("value-config-feature-octavo"))]
|
||||
Octavo,
|
||||
}
|
||||
|
||||
@@ -295,7 +305,7 @@ impl EnableCmd {
|
||||
if settings.daemon.enabled && !settings.daemon.autostart {
|
||||
println!("Restart the Atuin daemon and your shell for the change to take effect.");
|
||||
return backfilled;
|
||||
} else if let Err(e) = daemon::restart_cmd(settings).await {
|
||||
} else if let Err(e) = daemon::restart_cmd(settings, false).await {
|
||||
eprintln!(
|
||||
"Could not restart the Atuin daemon: {e}\nRun `atuin daemon restart` manually."
|
||||
);
|
||||
@@ -421,6 +431,72 @@ async fn onboard_octavo(settings: &Settings) -> Result<Option<OctavoOnboarding>>
|
||||
}))
|
||||
}
|
||||
|
||||
impl DisableCmd {
|
||||
pub async fn run(self, settings: &Settings) -> Result<()> {
|
||||
let config_file = Settings::get_config_path()?;
|
||||
let config_str = tokio::fs::read_to_string(&config_file).await?;
|
||||
|
||||
let updated = self.get_updated_config(&config_str)?;
|
||||
tokio::fs::write(&config_file, &updated).await?;
|
||||
|
||||
println!("Disabled.");
|
||||
|
||||
#[cfg(feature = "daemon")]
|
||||
match self.feature {
|
||||
Feature::Daemon => {
|
||||
if let Err(e) = daemon::stop_cmd(settings).await {
|
||||
eprintln!(
|
||||
"Could not stop the Atuin daemon: {e}\nRun `atuin daemon stop` manually."
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Feature::OutputCapture => restart_autostarted_daemon(settings).await,
|
||||
#[cfg(feature = "octavo")]
|
||||
Feature::Octavo => restart_autostarted_daemon(settings).await,
|
||||
}
|
||||
|
||||
println!("Restart your shell for the change to take effect.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_updated_config(&self, config_str: &str) -> Result<String> {
|
||||
let mut doc: DocumentMut = config_str.parse()?;
|
||||
|
||||
match self.feature {
|
||||
Feature::Daemon => {
|
||||
set_deep_key(&mut doc, "daemon.enabled", Value::from(false))?;
|
||||
set_deep_key(&mut doc, "daemon.autostart", Value::from(false))?;
|
||||
if doc.get("search_mode").and_then(Item::as_str) == Some("daemon-fuzzy") {
|
||||
set_deep_key(&mut doc, "search_mode", Value::from("fuzzy"))?;
|
||||
}
|
||||
}
|
||||
Feature::OutputCapture => {
|
||||
set_deep_key(&mut doc, "output.enabled", Value::from(false))?;
|
||||
}
|
||||
#[cfg(feature = "octavo")]
|
||||
Feature::Octavo => set_deep_key(&mut doc, "octavo.enabled", Value::from(false))?,
|
||||
}
|
||||
|
||||
let updated = doc.to_string();
|
||||
Settings::validate_str(&updated)
|
||||
.map_err(|e| eyre::eyre!("cannot update config: it would be invalid\n\n{e}"))?;
|
||||
|
||||
Ok(updated)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "daemon")]
|
||||
async fn restart_autostarted_daemon(settings: &Settings) {
|
||||
if settings.daemon.enabled
|
||||
&& settings.daemon.autostart
|
||||
&& let Err(e) = daemon::restart_cmd(settings, true).await
|
||||
{
|
||||
eprintln!("Could not restart the Atuin daemon: {e}\nRun `atuin daemon restart` manually.");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct PrintCmd {
|
||||
#[arg(help = fl!("arg-config-print-key"))]
|
||||
@@ -754,6 +830,27 @@ mod tests {
|
||||
assert_eq!(updated, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::daemon(Feature::Daemon, "", "[daemon]\nenabled = false\nautostart = false\n")]
|
||||
#[case::output_capture(Feature::OutputCapture, "", "[output]\nenabled = false\n")]
|
||||
#[case::daemon_search(
|
||||
Feature::Daemon,
|
||||
"search_mode = \"daemon-fuzzy\"\n[daemon]\nenabled = true",
|
||||
"search_mode = \"fuzzy\"\n[daemon]\nenabled = false\nautostart = false\n"
|
||||
)]
|
||||
#[case::non_daemon_search(
|
||||
Feature::Daemon,
|
||||
"search_mode = \"prefix\"\n[daemon]\nenabled = true",
|
||||
"search_mode = \"prefix\"\n[daemon]\nenabled = false\nautostart = false\n"
|
||||
)]
|
||||
fn disable_writes(#[case] feature: Feature, #[case] input: &str, #[case] expected: &str) {
|
||||
let updated = DisableCmd { feature }
|
||||
.get_updated_config(input)
|
||||
.expect("the update should be accepted");
|
||||
|
||||
assert_eq!(updated, expected);
|
||||
}
|
||||
|
||||
/// The error should always mention every listed fragment.
|
||||
#[rstest]
|
||||
#[case::an_invalid_value(
|
||||
|
||||
@@ -95,7 +95,7 @@ impl Cmd {
|
||||
Some(SubCmd::Start { force, .. }) => run(settings, store, history_db, force).await,
|
||||
Some(SubCmd::Status) => status_cmd(&settings).await,
|
||||
Some(SubCmd::Stop) => stop_cmd(&settings).await,
|
||||
Some(SubCmd::Restart) => restart_cmd(&settings).await,
|
||||
Some(SubCmd::Restart) => restart_cmd(&settings, false).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,7 +511,7 @@ async fn status_cmd(settings: &Settings) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn stop_cmd(settings: &Settings) -> Result<()> {
|
||||
pub(super) async fn stop_cmd(settings: &Settings) -> Result<()> {
|
||||
let Ok(mut client) = connect_client(settings).await else {
|
||||
println!("Daemon is not running");
|
||||
return Ok(());
|
||||
@@ -535,7 +535,7 @@ async fn stop_cmd(settings: &Settings) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn restart_cmd(settings: &Settings) -> Result<()> {
|
||||
pub(super) async fn restart_cmd(settings: &Settings, only_if_running: bool) -> Result<()> {
|
||||
// Stop if running
|
||||
match probe(settings).await {
|
||||
Probe::Ready(_) | Probe::NeedsRestart(_) => {
|
||||
@@ -550,6 +550,10 @@ pub(super) async fn restart_cmd(settings: &Settings) -> Result<()> {
|
||||
}
|
||||
Probe::Unreachable(_) => {
|
||||
println!("No daemon running");
|
||||
|
||||
if only_if_running {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2376,9 +2376,10 @@ fn set_clipboard(_s: String) -> Result<(), std::convert::Infallible> {
|
||||
mod tests {
|
||||
use atuin_client::database::Context;
|
||||
use atuin_client::history::History;
|
||||
#[cfg(unix)]
|
||||
use atuin_client::settings::RequestedSearchMode;
|
||||
use atuin_client::settings::{
|
||||
FilterMode, KeymapMode, Preview, PreviewStrategy, RequestedSearchMode, SearchMode,
|
||||
Settings, Shells,
|
||||
FilterMode, KeymapMode, Preview, PreviewStrategy, SearchMode, Settings, Shells,
|
||||
};
|
||||
use atuin_common::shell::Shell;
|
||||
use rstest::{fixture, rstest};
|
||||
|
||||
@@ -131,6 +131,24 @@ service manager such as systemd or `launchd` may run it. With `autostart` off,
|
||||
Atuin asks you to restart the daemon rather than doing it itself. Open a new
|
||||
shell afterward.
|
||||
|
||||
### `atuin config disable <feature>`
|
||||
|
||||
Turn off a feature.
|
||||
|
||||
```shell
|
||||
$ atuin config disable output-capture
|
||||
```
|
||||
|
||||
| Feature | What it sets |
|
||||
|------------------|--------------------------------------------------------------------------------------------------------------|
|
||||
| `daemon` | `daemon.enabled = false`, `daemon.autostart = false`, and `search_mode = "fuzzy"` if it was `"daemon-fuzzy"` |
|
||||
| `output-capture` | `output.enabled = false`. Leaves the daemon and search mode unchanged |
|
||||
|
||||
Disabling the daemon stops it if it was running.
|
||||
|
||||
Disabling output capture restarts an enabled daemon with `autostart` on so
|
||||
it picks up the change. Open a new shell afterward.
|
||||
|
||||
### `atuin config print [key]`
|
||||
|
||||
Print configuration values from your config file in TOML format. Without a
|
||||
|
||||
Reference in New Issue
Block a user