From f143e7976ca4cadb0ec257e58392b80b956ce1e7 Mon Sep 17 00:00:00 2001 From: MCorange Date: Sun, 7 Dec 2025 22:11:47 +0200 Subject: [PATCH] [2.1.1] Now with Updates! Added: - A working updater --- Cargo.lock | 35 ++++++++--- Cargo.toml | 8 ++- scripts/build_release.sh | 12 ++++ xmpd-core/Cargo.toml | 1 + xmpd-core/src/main.rs | 8 ++- xmpd-update/Cargo.toml | 15 +++++ xmpd-update/src/lib.rs | 123 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 189 insertions(+), 13 deletions(-) create mode 100755 scripts/build_release.sh create mode 100644 xmpd-update/Cargo.toml create mode 100644 xmpd-update/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 23c0936..4121cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3898,6 +3898,12 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.214" @@ -5750,7 +5756,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xmpd-cache" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "camino", @@ -5767,7 +5773,7 @@ dependencies = [ [[package]] name = "xmpd-cliargs" -version = "2.0.0" +version = "2.1.1" dependencies = [ "camino", "clap", @@ -5777,7 +5783,7 @@ dependencies = [ [[package]] name = "xmpd-core" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "camino", @@ -5789,11 +5795,12 @@ dependencies = [ "xmpd-gui", "xmpd-manifest", "xmpd-settings", + "xmpd-update", ] [[package]] name = "xmpd-gui" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "camino", @@ -5816,7 +5823,7 @@ dependencies = [ [[package]] name = "xmpd-manifest" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "serde", @@ -5830,7 +5837,7 @@ dependencies = [ [[package]] name = "xmpd-player" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "lazy_static", @@ -5840,7 +5847,7 @@ dependencies = [ [[package]] name = "xmpd-settings" -version = "2.0.0" +version = "2.1.1" dependencies = [ "anyhow", "camino", @@ -5853,7 +5860,19 @@ dependencies = [ [[package]] name = "xmpd-tooling" -version = "2.0.0" +version = "2.1.1" + +[[package]] +name = "xmpd-update" +version = "2.1.1" +dependencies = [ + "anyhow", + "log", + "reqwest", + "semver", + "serde", + "serde_json", +] [[package]] name = "zbus" diff --git a/Cargo.toml b/Cargo.toml index 975e680..3166733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ members=[ "xmpd-cache", "xmpd-settings", "xmpd-tooling", - "xmpd-player", + "xmpd-player", "xmpd-update", # "xmpd-tui" ] [workspace.package] -version="2.0.0" +version="2.1.1" edition="2024" repository="https://git.mcorangehq.xyz/XOR64/xmpd/" license="GPL-3.0" @@ -36,7 +36,7 @@ lazy_static = "1.4.0" log = "0.4.21" # notify-rust = "4.11.3" # open = "5.3.0" -reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls"], default-features = false } +reqwest = { version = "0.12.3", features = ["blocking", "h2", "http2", "rustls-tls", "json"], default-features = false } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" url = { version = "2.5.0", features = ["serde"] } @@ -50,3 +50,5 @@ rfd = "0.15.1" rodio = { version = "0.20.1", features = ["symphonia-all"] } image = "0.25.5" downcast-rs = "2.0.2" +semver = "1.0.27" +parse-changelog = "0.6.14" diff --git a/scripts/build_release.sh b/scripts/build_release.sh new file mode 100755 index 0000000..75c78ab --- /dev/null +++ b/scripts/build_release.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash + +set -x +rm target/xmpd target/xmpd.exe + +set -e + +cargo build --release --target x86_64-pc-windows-gnu +cargo build --release --target x86_64-unknown-linux-gnu + +cp target/x86_64-unknown-linux-gnu/release/xmpd target/xmpd +cp target/x86_64-pc-windows-gnu/release/xmpd.exe target/xmpd.exe diff --git a/xmpd-core/Cargo.toml b/xmpd-core/Cargo.toml index 51e49e9..34b5066 100644 --- a/xmpd-core/Cargo.toml +++ b/xmpd-core/Cargo.toml @@ -25,6 +25,7 @@ xmpd-cliargs.path="../xmpd-cliargs" xmpd-gui.path="../xmpd-gui" xmpd-manifest.path="../xmpd-manifest" xmpd-settings.path = "../xmpd-settings" +xmpd-update.path = "../xmpd-update" clap.workspace=true camino.workspace = true anyhow.workspace = true diff --git a/xmpd-core/src/main.rs b/xmpd-core/src/main.rs index e6b4c2a..feb6b9d 100644 --- a/xmpd-core/src/main.rs +++ b/xmpd-core/src/main.rs @@ -10,12 +10,16 @@ fn main() -> Result<()> { // NOTE: Parses on first load let cliargs = &xmpd_cliargs::CLIARGS; logger::init(&cliargs); - log::debug!("Initialising settings"); + log::info!("Initialising settings"); { xmpd_settings::Settings::get()?.load(Some(cliargs.settings_path().into_std_path_buf()))?; xmpd_settings::Settings::get()?.load_cli_args(cliargs); } - log::debug!("Starting gui"); + + log::info!("Starting updater"); + xmpd_update::Update::new().update_xmpd_if_needed()?; + + log::info!("Starting gui"); xmpd_gui::start()?; Ok(()) } diff --git a/xmpd-update/Cargo.toml b/xmpd-update/Cargo.toml new file mode 100644 index 0000000..e56df8d --- /dev/null +++ b/xmpd-update/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "xmpd-update" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +reqwest.workspace = true +anyhow.workspace = true +serde.workspace = true +serde_json.workspace = true +semver.workspace = true +log.workspace = true diff --git a/xmpd-update/src/lib.rs b/xmpd-update/src/lib.rs new file mode 100644 index 0000000..b3fed80 --- /dev/null +++ b/xmpd-update/src/lib.rs @@ -0,0 +1,123 @@ +#[cfg(target_family = "unix")] +use std::{fs::Permissions, os::unix::fs::PermissionsExt}; +use std::{env::args, path::PathBuf, process::{Stdio, exit}, str::FromStr}; + +use anyhow::anyhow; +use reqwest::blocking::Client; +use serde::Deserialize; + + +const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); +const GIT_HOST: &'static str = "git.mcorangehq.xyz"; +const GIT_USER: &'static str = "XOR64"; +const GIT_REPO: &'static str = "xmpd"; + +#[cfg(target_family = "windows")] +const EXT: &'static str = ".exe"; +#[cfg(target_family = "unix")] +const EXT: &'static str = ""; + + + +pub type Result = anyhow::Result; + +#[derive(Debug, Deserialize)] +struct ReleaseInfo { + tag_name: String, + assets: Vec, +} + +#[derive(Debug, Deserialize)] +struct ReleaseAsset { + browser_download_url: String, + name: String, +} + + +pub struct Update { + +} + +impl Update { + pub fn new() -> Self { + Self {} + } + pub fn update_xmpd_if_needed(&self) -> Result<()> { + let current = semver::Version::parse(CURRENT_VERSION)?; + log::info!("Current xmpd version: {current}"); + let client = Client::new(); + let req = client + .get(format!("https://{GIT_HOST}/api/v1/repos/{GIT_USER}/{GIT_REPO}/releases/latest")) + .send(); + + let Ok(req) = req else { + log::warn!("Unable to check latest release! (No internet?)"); + return Ok(()); + }; + + let exec_p = std::env::current_exe()?; + let exec_d = exec_p.parent().unwrap_or(&exec_p); + + let release: ReleaseInfo = req.json().map_err(|e| anyhow!("Unable to parse release info: {e}"))?; + let latest = semver::Version::parse(&release.tag_name)?; + + + log::info!("Latest xmpd version: {latest}"); + if latest > current { + log::warn!("Update available, proceeding to update!"); + for asset in release.assets { + if asset.name != format!("xmpd{EXT}") { + continue; + } + let tmp_f = exec_d.join(format!("xmpd.update{EXT}")); + let old_f = exec_d.join(format!("xmpd.old{EXT}")); + log::info!("Downloading {}...", asset.browser_download_url); + let mut res; + match reqwest::blocking::get(asset.browser_download_url) { + Ok(r) => res = r, + Err(e) => { + log::error!("Failed to download update: {e}"); + return Ok(()); + } + } + + let mut tmp_h = std::fs::File::create(&tmp_f)?; + if let Err(e) = std::io::copy(&mut res, &mut tmp_h) { + log::error!("Failed to write update to file: {e}"); + return Ok(()); + } + + drop(tmp_h); // close handle (just in case you didnt know) + + #[cfg(target_family = "unix")] + { + // set *only* the exec bit, doesnt change other permissions + let meta = std::fs::metadata(&tmp_f)?; + let mut perms = meta.permissions(); + perms.set_mode(perms.mode() | 0o111); + + std::fs::set_permissions(&tmp_f, perms)?; + } + std::fs::rename(&exec_p, &old_f)?; + std::fs::rename(&tmp_f, &exec_p)?; + + log::debug!("{exec_p:?} -> {old_f:?}"); + log::debug!("{tmp_f:?} -> {exec_p:?}"); + log::info!("Update done, restarting!"); + + let status = std::process::Command::new(exec_p) + .args(&args().collect::>().as_slice()[1..]) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .status()?; + exit(status.code().unwrap_or(0)); + } + + } else { + log::info!("Already at latest version, skipping update!"); + } + Ok(()) + } + +}