Fixed auto update
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1123,7 +1123,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ostiary"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"crossterm",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ostiary"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
16
src/app.rs
16
src/app.rs
@@ -19,7 +19,8 @@ pub enum Event {
|
||||
StructuredFinished(i32),
|
||||
UpdateAvailable(updater::UpdateInfo),
|
||||
UpdateProgress(u64),
|
||||
UpdateDone,
|
||||
/// Carries the exe path captured before the binary was replaced.
|
||||
UpdateDone(std::path::PathBuf),
|
||||
UpdateError(String),
|
||||
}
|
||||
|
||||
@@ -40,7 +41,8 @@ pub struct App {
|
||||
pub event_tx: UnboundedSender<Event>,
|
||||
/// When set, main loop suspends ratatui, runs the command, then restores.
|
||||
pub pending_exec: Option<PendingExec>,
|
||||
pub pending_restart: bool,
|
||||
/// Exe path to exec after update; captured before the binary was replaced.
|
||||
pub pending_restart: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
pub enum Popup {
|
||||
@@ -104,7 +106,7 @@ impl App {
|
||||
popup: None,
|
||||
event_tx,
|
||||
pending_exec: None,
|
||||
pending_restart: false,
|
||||
pending_restart: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,11 +243,11 @@ impl App {
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
Event::UpdateDone => {
|
||||
Event::UpdateDone(exe_path) => {
|
||||
if let Some(Popup::Updating { status, .. }) = &mut self.popup {
|
||||
*status = UpdatingStatus::Done;
|
||||
}
|
||||
self.pending_restart = true;
|
||||
self.pending_restart = Some(exe_path);
|
||||
Ok(false)
|
||||
}
|
||||
Event::UpdateError(msg) => {
|
||||
@@ -507,8 +509,8 @@ impl App {
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
let _ = tx2.send(Event::UpdateDone);
|
||||
Ok(exe_path) => {
|
||||
let _ = tx2.send(Event::UpdateDone(exe_path));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ =
|
||||
|
||||
@@ -74,7 +74,7 @@ async fn run_app(
|
||||
}
|
||||
|
||||
// Check for pending restart (after update applied) — before pending_exec.
|
||||
if app.pending_restart {
|
||||
if let Some(exe_path) = app.pending_restart.take() {
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
@@ -83,7 +83,7 @@ async fn run_app(
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
#[cfg(unix)]
|
||||
updater::exec_updated();
|
||||
updater::exec_updated(&exe_path);
|
||||
// fallback for non-unix or if exec failed
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -94,11 +94,12 @@ pub async fn check(api_base: &str, timeout_sec: u64) -> Result<Option<UpdateInfo
|
||||
|
||||
/// Downloads binary to temp file with streaming progress, atomically replaces
|
||||
/// current exe, sets chmod 755.
|
||||
/// progress_tx receives bytes downloaded so far.
|
||||
/// Returns the path of the replaced executable (captured before rename so it
|
||||
/// remains valid even after the old inode is marked "(deleted)" by the kernel).
|
||||
pub async fn download_and_apply(
|
||||
info: &UpdateInfo,
|
||||
progress_tx: UnboundedSender<u64>,
|
||||
) -> Result<()> {
|
||||
) -> Result<std::path::PathBuf> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let response = client
|
||||
@@ -144,25 +145,28 @@ pub async fn download_and_apply(
|
||||
std::fs::set_permissions(&tmp_path, perms).context("failed to set permissions")?;
|
||||
}
|
||||
|
||||
// Atomically replace current exe
|
||||
// Atomically replace current exe.
|
||||
// current_exe is captured BEFORE this rename — after rename, /proc/self/exe
|
||||
// on Linux returns the path with " (deleted)" appended, but the PathBuf we
|
||||
// hold still refers to the correct filesystem path of the new binary.
|
||||
std::fs::rename(&tmp_path, ¤t_exe).context("failed to replace current exe")?;
|
||||
|
||||
Ok(())
|
||||
Ok(current_exe)
|
||||
}
|
||||
|
||||
/// Replaces current process via execv (Unix). Never returns on success.
|
||||
/// `exe_path` must be the path captured *before* the binary was replaced —
|
||||
/// do NOT call std::env::current_exe() here, it returns "(deleted)" on Linux.
|
||||
#[cfg(unix)]
|
||||
pub fn exec_updated() -> ! {
|
||||
pub fn exec_updated(exe_path: &std::path::Path) -> ! {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
let exe = std::env::current_exe().expect("failed to get current exe path");
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
|
||||
let err = std::process::Command::new(&exe)
|
||||
let err = std::process::Command::new(exe_path)
|
||||
.args(&args[1..])
|
||||
.exec();
|
||||
|
||||
// exec only returns if it failed
|
||||
eprintln!("Failed to exec updated binary: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user