1 Commits

Author SHA1 Message Date
Uber Veng
b395df590e added version number, fixed infinite update loop 2026-05-22 01:26:21 +07:00
6 changed files with 70 additions and 4 deletions

2
Cargo.lock generated
View File

@@ -1123,7 +1123,7 @@ dependencies = [
[[package]]
name = "ostiary"
version = "1.0.5"
version = "1.0.7"
dependencies = [
"anyhow",
"crossterm",

View File

@@ -1,6 +1,6 @@
[package]
name = "ostiary"
version = "1.0.6"
version = "1.0.7"
edition = "2024"
[dependencies]

View File

@@ -517,6 +517,12 @@ impl App {
.await
{
Ok(exe_path) => {
// Write expected version before exec so
// the next startup can detect a failed
// replacement (wrong asset, etc.).
updater::write_update_target(
&info_clone.new_version,
);
let _ = tx2.send(Event::UpdateDone(exe_path));
}
Err(e) => {

View File

@@ -35,8 +35,26 @@ async fn main() -> Result<()> {
let (event_tx, mut event_rx) = mpsc::unbounded_channel();
let mut app = app::App::new(config, event_tx.clone());
// Detect failed update: if the previous run wrote an expected version but
// the actual version didn't change, the replacement didn't work.
// Show an error and skip the update check to break any infinite loop.
let update_ok = match updater::take_update_target() {
Some(expected) if expected != env!("CARGO_PKG_VERSION") => {
app.error = Some(format!(
"Обновление не применилось: ожидалась v{expected}, \
запущена v{}. Проверьте, что в релизе загружен правильный бинарник.",
env!("CARGO_PKG_VERSION")
));
false
}
_ => true,
};
app.load_menu().await;
app.check_update();
if update_ok {
app.check_update();
}
let res = run_app(&mut terminal, &mut app, &mut event_rx).await;

View File

@@ -48,7 +48,18 @@ fn render_menu(f: &mut Frame, app: &App, area: Rect) {
.collect();
let list = List::new(list_items)
.block(Block::default().borders(Borders::ALL).title("Меню"))
.block(
Block::default()
.borders(Borders::ALL)
.title(" Меню ")
.title(
Line::from(Span::styled(
format!(" v{} ", env!("CARGO_PKG_VERSION")),
Style::default().fg(Color::DarkGray),
))
.alignment(Alignment::Right),
),
)
.highlight_style(Style::default().add_modifier(Modifier::BOLD));
f.render_widget(list, area);
}

View File

@@ -154,6 +154,37 @@ pub async fn download_and_apply(
Ok(current_exe)
}
// ── Update-target file ───────────────────────────────────────────────────────
// Written just before exec. On next startup we compare the expected version
// with CARGO_PKG_VERSION. A mismatch means the replacement didn't work
// (wrong asset, failed rename, etc.) and we show an error instead of looping.
fn update_target_path() -> std::path::PathBuf {
crate::config::config_path()
.parent()
.expect("config dir has parent")
.join(".update_target")
}
/// Write the expected version to disk before exec'ing the new binary.
pub fn write_update_target(new_version: &str) {
let _ = std::fs::write(update_target_path(), new_version);
}
/// Read and delete the update-target file.
/// Returns `Some(expected_version)` if a previous run attempted an update.
/// The caller should compare it with `env!("CARGO_PKG_VERSION")` and show
/// an error if they differ.
pub fn take_update_target() -> Option<String> {
let path = update_target_path();
if !path.exists() {
return None;
}
let version = std::fs::read_to_string(&path).ok()?;
let _ = std::fs::remove_file(&path);
Some(version.trim().to_string())
}
/// 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.