diff options
| author | Lucas Trzesniewski <lucas.trzesniewski@gmail.com> | 2026-05-04 22:12:12 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-04 13:12:12 -0700 |
| commit | 9609759d775c8851d84eadf983c51db2798ebc81 (patch) | |
| tree | 5edfa3bbc42f93cdbde7ecbe503f70a32ee18bbd /crates/atuin-client | |
| parent | chore(release): prepare for release 18.16.0 (#3457) (diff) | |
| download | atuin-9609759d775c8851d84eadf983c51db2798ebc81.zip | |
fix: atuin update on windows (#3453)
This fixes the `atuin update` command on Windows.
Windows doesn't let you overwrite a running exe, but it lets you rename
it. This PR special-cases the official `update` plugin by renaming the
running `atuin.exe` to `atuin.old` before the update, and rolling it
back if the update fails.
Note that the `atuin.old` file is left behind on success, which
shouldn't be a problem in practice: it will be overwritten on the next
call to `atuin update` (also deleted if there's no update available),
and is located in `~/.atuin/bin`, which is an isolated location specific
to Atuin.
Fixes #3451
## 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
Diffstat (limited to 'crates/atuin-client')
| -rw-r--r-- | crates/atuin-client/src/plugin.rs | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/crates/atuin-client/src/plugin.rs b/crates/atuin-client/src/plugin.rs index 21a2bcef..6f351bf1 100644 --- a/crates/atuin-client/src/plugin.rs +++ b/crates/atuin-client/src/plugin.rs @@ -42,7 +42,7 @@ impl OfficialPluginRegistry { "Update atuin to the latest version", "The 'atuin update' command is provided by the atuin-update plugin.\n\ It is only installed if you used the install script\n \ - If you used a package manager (brew, apt, etc), please continue to use it for updates" + If you used a package manager (brew, apt, etc), please continue to use it for updates", ), ); } @@ -68,6 +68,59 @@ impl Default for OfficialPluginRegistry { } } +pub struct PluginContext { + #[cfg(windows)] + _update_on_windows: Option<UpdateOnWindowsContext>, +} + +impl PluginContext { + pub fn new(_subcommand: &str) -> Self { + PluginContext { + #[cfg(windows)] + _update_on_windows: (_subcommand == "update").then(UpdateOnWindowsContext::new), + } + } +} + +impl Drop for PluginContext { + fn drop(&mut self) {} +} + +#[cfg(windows)] +struct UpdateOnWindowsContext { + initial_exe: Option<std::path::PathBuf>, +} + +#[cfg(windows)] +impl UpdateOnWindowsContext { + const OLD_FILE_NAME: &'static str = "atuin.old"; + + pub fn new() -> Self { + // Windows doesn't let you overwrite a running exe, but it lets you rename it, + // so make some room for atuin-update to install the new version. + let initial_exe = std::env::current_exe().ok().and_then(|exe| { + std::fs::rename(&exe, exe.with_file_name(Self::OLD_FILE_NAME)).ok()?; + Some(exe) + }); + + Self { initial_exe } + } +} + +#[cfg(windows)] +impl Drop for UpdateOnWindowsContext { + fn drop(&mut self) { + if let Some(exe) = &self.initial_exe + && !exe.exists() + { + // The update failed, roll back the current exe to its initial name. + std::fs::rename(exe.with_file_name(Self::OLD_FILE_NAME), exe).unwrap_or_else(|e| { + eprintln!("Failed to roll back the update, you may need to reinstall Atuin: {e}"); + }); + } + } +} + #[cfg(test)] mod tests { use super::*; |
