diff --git a/aw-server/src/android/mod.rs b/aw-server/src/android/mod.rs index 58a50456..2386a56a 100644 --- a/aw-server/src/android/mod.rs +++ b/aw-server/src/android/mod.rs @@ -172,6 +172,20 @@ pub mod android { dirs::set_android_data_dir(path); } + /// Report the Android app's release version from `/api/0/info` instead of + /// the aw-server-rust package version, which is the version of a component + /// rather than of the app the user installed. + #[no_mangle] + pub unsafe extern "C" fn Java_net_activitywatch_android_RustInterface_setVersionOverride( + env: JNIEnv, + _: JClass, + java_version: JString, + ) { + let version = &jstring_to_string(&env, java_version); + debug!("Setting reported version to {}", version); + crate::version::set_version_override(version); + } + #[no_mangle] pub unsafe extern "C" fn Java_net_activitywatch_android_RustInterface_getBuckets( env: JNIEnv, diff --git a/aw-server/src/endpoints/mod.rs b/aw-server/src/endpoints/mod.rs index 8d098f7c..fbd5759b 100644 --- a/aw-server/src/endpoints/mod.rs +++ b/aw-server/src/endpoints/mod.rs @@ -112,11 +112,10 @@ fn root_manifest(state: &State) -> Option<(ContentType, Vec)> { fn server_info(config: &State, state: &State) -> Json { #[allow(clippy::or_fun_call)] let hostname = gethostname().into_string().unwrap_or("unknown".to_string()); - const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); Json(Info { hostname, - version: format!("v{} (rust)", VERSION.unwrap_or("(unknown)")), + version: crate::version::version_string(), testing: config.testing, device_id: state.device_id.clone(), }) diff --git a/aw-server/src/lib.rs b/aw-server/src/lib.rs index 2755d536..deb0547e 100644 --- a/aw-server/src/lib.rs +++ b/aw-server/src/lib.rs @@ -24,6 +24,7 @@ pub mod device_id; pub mod dirs; pub mod endpoints; pub mod logging; +pub mod version; #[cfg(target_os = "android")] pub mod android; diff --git a/aw-server/src/version.rs b/aw-server/src/version.rs new file mode 100644 index 00000000..b32d3473 --- /dev/null +++ b/aw-server/src/version.rs @@ -0,0 +1,62 @@ +//! Version string reported by `GET /api/0/info`. +//! +//! By default this is the aw-server-rust package version. That is wrong for +//! builds that embed aw-server-rust as a component of a larger product: on +//! Android the webui footer showed `v0.14.0 (rust)` (the Cargo.toml version) +//! while the installed app was `v0.14.0b2`. +//! +//! Such builds call [`set_version_override`] at startup to report their own +//! release version instead. + +use std::sync::RwLock; + +static VERSION_OVERRIDE: RwLock> = RwLock::new(None); + +/// Report `version` from `/api/0/info` instead of the package version. +/// +/// The string is used verbatim, so the caller controls the exact format +/// (including any `v` prefix). +pub fn set_version_override(version: &str) { + let mut guard = VERSION_OVERRIDE.write().unwrap(); + *guard = Some(version.to_string()); +} + +/// The version string to report from `/api/0/info`. +pub fn version_string() -> String { + if let Some(version) = VERSION_OVERRIDE.read().unwrap().as_ref() { + return version.clone(); + } + const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); + format!("v{} (rust)", VERSION.unwrap_or("(unknown)")) +} + +#[cfg(test)] +mod tests { + use super::*; + + struct VersionGuard; + impl Drop for VersionGuard { + fn drop(&mut self) { + *VERSION_OVERRIDE.write().unwrap() = None; + } + } + + // These share process-global state, so they run as one test. + #[test] + fn override_replaces_package_version() { + let _guard = VersionGuard; // resets VERSION_OVERRIDE on exit, even on panic + + assert!( + version_string().ends_with(" (rust)"), + "default should report the package version, got {:?}", + version_string() + ); + + set_version_override("v0.14.0b2"); + assert_eq!(version_string(), "v0.14.0b2"); + + // Used verbatim: the caller owns the format. + set_version_override("1.2.3-custom"); + assert_eq!(version_string(), "1.2.3-custom"); + } +}