From b97c7919bd9ce61b89347651f308e0b5b1cb77a9 Mon Sep 17 00:00:00 2001 From: Mac Date: Mon, 4 May 2026 17:16:30 -0600 Subject: [PATCH] feat: send current_patch_number on patch check (#343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: send current patch_number on patch check * refactor: rename patch_number → current_patch_number Renaming the field on PatchCheckRequest so it doesn't collide with the legacy `patch_number` field old pre-#189 updaters still send. That legacy field is what triggers the server's short-circuit response (patchAvailable: false, no rolled_back_patch_numbers), which is still load-bearing for ~0.7% of patch-check traffic coming from Flutter ≤ 3.22.2 clients. Using a distinct field name keeps our new analytics signal from accidentally engaging that path. Pairs with: - shorebirdtech/shorebird#3702 (protocol field) - shorebirdtech/_shorebird#2059 (server consumes this field) * docs: rephrase current_patch_number doc comment Drops the 'analytics' framing in favor of describing the field as superseding patch_number for newer clients. patch_number remains for compatibility with legacy clients that rely on the short-circuit path. * fix: adapt to renamed currently_booting_patch and &str client_id --- library/src/network.rs | 18 +++++++++++++++--- library/src/updater.rs | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/library/src/network.rs b/library/src/network.rs index 2248bd3..1504bc8 100644 --- a/library/src/network.rs +++ b/library/src/network.rs @@ -247,12 +247,22 @@ pub struct PatchCheckRequest { /// The unique ID of this device. This is a random UUID generated by Shorebird and _not_ the /// device's UUID or any other identifier that has meaning outside of Shorebird. pub client_id: String, - // We specifically do not send a patch number as part of this request because we always want to - // know what the latest available patch is. + /// The number of the patch currently running on the device, if any. + /// + /// Supersedes the legacy `patch_number` field. `patch_number` is + /// retained server-side for compatibility with older clients that still + /// rely on its short-circuit response path; newer updaters populate + /// this field instead. + #[serde(skip_serializing_if = "Option::is_none")] + pub current_patch_number: Option, } impl PatchCheckRequest { - pub fn new(config: &UpdateConfig, client_id: &str) -> PatchCheckRequest { + pub fn new( + config: &UpdateConfig, + client_id: &str, + current_patch_number: Option, + ) -> PatchCheckRequest { PatchCheckRequest { app_id: config.app_id.clone(), channel: config.channel.clone(), @@ -260,6 +270,7 @@ impl PatchCheckRequest { platform: current_platform().to_string(), arch: current_arch().to_string(), client_id: client_id.to_string(), + current_patch_number, } } } @@ -404,6 +415,7 @@ mod tests { platform: "".to_string(), arch: "".to_string(), client_id: "".to_string(), + current_patch_number: None, }, ); assert!(result.is_err()); diff --git a/library/src/updater.rs b/library/src/updater.rs index bcde590..e7c5aa8 100644 --- a/library/src/updater.rs +++ b/library/src/updater.rs @@ -273,7 +273,12 @@ pub fn should_auto_update() -> anyhow::Result { /// Returns true if an update is available for download. Will return false if the update is already /// downloaded and ready to install. pub fn check_for_downloadable_update(channel: Option<&str>) -> anyhow::Result { - let client_id = with_state(|state| Ok(state.client_id()))?; + let (client_id, current_patch_number) = with_state(|state| { + Ok(( + state.client_id().to_string(), + state.currently_booting_patch().map(|p| p.number), + )) + })?; let (request, url, request_fn) = with_config(|config| { let mut config = config.clone(); @@ -283,7 +288,7 @@ pub fn check_for_downloadable_update(channel: Option<&str>) -> anyhow::Result) -> anyhow::Resul shorebird_error!("Failed to clear events: {:?}", err); } // Update our outer state with the new state. - Ok(PatchCheckRequest::new(&config, &state.client_id())) + Ok(PatchCheckRequest::new( + &config, + &state.client_id(), + state.currently_booting_patch().map(|p| p.number), + )) })?; // Check for update.