Skip to content

Commit

Permalink
feat: use self_replace to delete self
Browse files Browse the repository at this point in the history
  • Loading branch information
mistydemeo committed Sep 16, 2024
1 parent 04c6667 commit 923a982
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 23 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions axoupdater/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ tokio = { version = "1.36.0", features = ["full"], optional = true }
# errors
miette = "7.2.0"
thiserror = "1.0.58"

[target.'cfg(windows)'.dependencies]
self-replace = "1.5.0"
7 changes: 7 additions & 0 deletions axoupdater/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,11 @@ pub enum AxoupdateError {
/// to the terminal when running the installer.
stderr: Option<String>,
},

/// self_replace/self_delete failed
#[error(
"Cleaning up the previous version failed; a copy of the old version has been left behind."
)]
#[diagnostic(help("This probably isn't your fault; please open an issue at https://github.com/axodotdev/axoupdater!"))]
CleanupFailed {},
}
40 changes: 17 additions & 23 deletions axoupdater/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ pub use release::*;

use std::{
env::{self, args},
ffi::OsStr,
process::Stdio,
};

#[cfg(unix)]
use std::{fs::File, os::unix::fs::PermissionsExt};

#[cfg(windows)]
use self_replace;

use axoasset::LocalAsset;
use axoprocess::Cmd;
pub use axotag::Version;
Expand Down Expand Up @@ -453,34 +457,21 @@ impl AxoUpdater {
installer_path
};

// Before we update, move ourselves to a temporary directory.
// Before we update, rename ourselves to a temporary name.
// This is necessary because Windows won't let an actively-running
// executable be overwritten.
// If the update fails, we'll move it back to where it was before
// we began the update process.
// NOTE: this TempDir needs to be held alive for the whole function.
let temp_root;
let to_restore = if cfg!(target_family = "windows") {
// If we know the install prefix, place the temporary directory for
// the executable there. This is because the `rename` syscall can't
// rename an executable across filesystem bounds, and there's a
// chance the system temporary directory could be on a different
// drive than the executable is.
// (A copy-and-delete move won't work because Windows won't let us
// delete a running executable - the same reason we're moving it out
// of the way to begin with!)
// See https://github.com/axodotdev/axoupdater/issues/120.
temp_root = if let Ok(prefix) = self.install_prefix_root() {
TempDir::new_in(prefix)?
} else {
TempDir::new()?
};
let old_path = std::env::current_exe()?;
let old_filename = old_path.file_name().expect("current binary has no name!?");
let ourselves = temp_root.path().join(old_filename);
std::fs::rename(&old_path, &ourselves)?;
let old_path;
let to_restore = if cfg!(windows) {
old_path = std::env::current_exe()?;
let filename = old_path.file_name();
let old_filename = filename.expect("current binary has no name!?");

Some((ourselves, old_path))
let mut new_filename = old_filename.to_os_string();
new_filename.push(OsStr::new(".__selfdelete__.exe"));

Some((new_filename, old_filename))
} else {
None
};
Expand Down Expand Up @@ -550,6 +541,9 @@ impl AxoUpdater {
if let Some((ourselves, old_path)) = to_restore {
std::fs::rename(ourselves, old_path)?;
}
} else {
#[cfg(windows)]
self_replace::self_delete().map_err(|_| AxoupdateError::CleanupFailed {})?;
}

// Return the original AxoprocessError if we failed to launch
Expand Down

0 comments on commit 923a982

Please sign in to comment.