From 3f80bee53a5f3acb273c01fdf98fccaf877793e3 Mon Sep 17 00:00:00 2001 From: Roey Darwish Dror Date: Wed, 1 Jul 2020 21:03:19 +0300 Subject: [PATCH] Self rename in Windows (fix #458) (#460) --- config.example.toml | 6 +++++- src/config.rs | 11 +++++++++++ src/main.rs | 12 ++++++++++++ src/self_renamer.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/self_renamer.rs diff --git a/config.example.toml b/config.example.toml index 39b7ee94..3833d5b3 100644 --- a/config.example.toml +++ b/config.example.toml @@ -66,5 +66,9 @@ #[windows] # Manually select Windows updates -# accept_all_updates = false +#accept_all_updates = false +# Causes Topgrade to rename itself during the run to allow package managers +# to upgrade it. Use this only if you installed Topgrade by using a package +# manager such as Scoop to Cargo +#self_rename = true diff --git a/src/config.rs b/src/config.rs index d10168f4..1c115fbe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -126,6 +126,7 @@ pub struct Vagrant { #[serde(deny_unknown_fields)] pub struct Windows { accept_all_updates: Option, + self_rename: Option, } #[derive(Deserialize, Default, Debug)] @@ -505,6 +506,16 @@ impl Config { .unwrap_or(true) } + /// Whether to self rename the Topgrade executable during the run + #[allow(dead_code)] + pub fn self_rename(&self) -> bool { + self.config_file + .windows + .as_ref() + .and_then(|w| w.self_rename) + .unwrap_or(false) + } + /// Whether Brew cask should be greedy #[allow(dead_code)] pub fn brew_cask_greedy(&self) -> bool { diff --git a/src/main.rs b/src/main.rs index 2973771b..f9dac9fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ mod execution_context; mod executor; mod report; mod runner; +#[cfg(windows)] +mod self_renamer; #[cfg(feature = "self-update")] mod self_update; mod steps; @@ -92,6 +94,16 @@ fn run() -> Result<()> { } } + #[cfg(windows)] + let _self_rename = if config.self_rename() { + Some(crate::self_renamer::SelfRenamer::create()?) + } else { + None + }; + + let mut s = String::new(); + std::io::stdin().read_line(&mut s)?; + if let Some(commands) = config.pre_commands() { for (name, command) in commands { generic::run_custom_command(&name, &command, &ctx)?; diff --git a/src/self_renamer.rs b/src/self_renamer.rs new file mode 100644 index 00000000..ef556390 --- /dev/null +++ b/src/self_renamer.rs @@ -0,0 +1,43 @@ +#![cfg(windows)] + +use anyhow::Result; +use log::{debug, error}; +use std::{env::current_exe, fs, path::PathBuf}; + +pub struct SelfRenamer { + exe_path: PathBuf, + temp_path: PathBuf, +} + +impl SelfRenamer { + pub fn create() -> Result { + let tempdir = tempfile::tempdir()?; + let temp_path = tempdir.path().join("topgrade.exe"); + let exe_path = current_exe()?; + + debug!("Current exe in {:?}. Moving it to {:?}", exe_path, temp_path); + + fs::rename(&exe_path, &temp_path)?; + + Ok(SelfRenamer { exe_path, temp_path }) + } +} + +impl Drop for SelfRenamer { + fn drop(&mut self) { + if self.exe_path.exists() { + debug!("{:?} exists. Topgrade was probably upgraded", self.exe_path); + return; + } + + match fs::rename(&self.temp_path, &self.exe_path) { + Ok(_) => debug!("Moved topgrade back from {:?} to {:?}", self.temp_path, self.exe_path), + Err(e) => error!( + "Could not move Topgrade from {} back to {}: {}", + self.temp_path.display(), + self.exe_path.display(), + e + ), + } + } +}