Skip to content

Commit 579312f

Browse files
authored
feat(updater): separate intel and apple silicon targets, closes #3359 (#3739)
1 parent bf89a05 commit 579312f

File tree

8 files changed

+175
-67
lines changed

8 files changed

+175
-67
lines changed

.changes/custom-updater-target.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Added `updater_target` method to the `Builder` struct.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
**Breaking change:** The updater default targets have been renamed to include better support for different architectures.

core/tauri-utils/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,6 +1822,17 @@ pub struct UpdaterConfig {
18221822
#[serde(default = "default_dialog")]
18231823
pub dialog: bool,
18241824
/// The updater endpoints. TLS is enforced on production.
1825+
///
1826+
/// The updater URL can contain the following variables:
1827+
/// - {{current_version}}: The version of the app that is requesting the update
1828+
/// - {{target}}: The operating system name (one of `linux`, `windows` or `darwin`).
1829+
/// - {{arch}}: The architecture of the machine (one of `x86_64`, `i686`, `aarch64` or `armv7`).
1830+
///
1831+
/// # Examples
1832+
///
1833+
/// - "https://my.cdn.com/latest.json": a raw JSON endpoint that returns the latest version and download links for each platform.
1834+
/// - "https://updates.app.dev/{{target}}?version={{current_version}}&arch={{arch}}": a dedicated API with positional and query string arguments.
1835+
#[allow(rustdoc::bare_urls)]
18251836
pub endpoints: Option<Vec<UpdaterEndpoint>>,
18261837
/// Signature public key.
18271838
#[serde(default)] // use default just so the schema doesn't flag it as required

core/tauri/src/app.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ impl<R: Runtime> GlobalWindowEvent<R> {
167167
}
168168
}
169169

170+
#[cfg(updater)]
171+
#[derive(Debug, Clone, Default)]
172+
pub(crate) struct UpdaterSettings {
173+
pub(crate) target: Option<String>,
174+
}
175+
170176
/// The path resolver is a helper for the application-specific [`crate::api::path`] APIs.
171177
#[derive(Debug, Clone)]
172178
pub struct PathResolver {
@@ -217,6 +223,9 @@ pub struct AppHandle<R: Runtime> {
217223
clipboard_manager: R::ClipboardManager,
218224
#[cfg(feature = "system-tray")]
219225
tray_handle: Option<tray::SystemTrayHandle<R>>,
226+
/// The updater configuration.
227+
#[cfg(updater)]
228+
pub(crate) updater_settings: UpdaterSettings,
220229
}
221230

222231
impl<R: Runtime> AppHandle<R> {
@@ -264,6 +273,8 @@ impl<R: Runtime> Clone for AppHandle<R> {
264273
clipboard_manager: self.clipboard_manager.clone(),
265274
#[cfg(feature = "system-tray")]
266275
tray_handle: self.tray_handle.clone(),
276+
#[cfg(updater)]
277+
updater_settings: self.updater_settings.clone(),
267278
}
268279
}
269280
}
@@ -677,6 +688,10 @@ pub struct Builder<R: Runtime> {
677688
/// System tray event handlers.
678689
#[cfg(feature = "system-tray")]
679690
system_tray_event_listeners: Vec<SystemTrayEventListener<R>>,
691+
692+
/// The updater configuration.
693+
#[cfg(updater)]
694+
updater_settings: UpdaterSettings,
680695
}
681696

682697
impl<R: Runtime> Builder<R> {
@@ -702,6 +717,8 @@ impl<R: Runtime> Builder<R> {
702717
system_tray: None,
703718
#[cfg(feature = "system-tray")]
704719
system_tray_event_listeners: Vec::new(),
720+
#[cfg(updater)]
721+
updater_settings: Default::default(),
705722
}
706723
}
707724

@@ -1087,6 +1104,45 @@ impl<R: Runtime> Builder<R> {
10871104
self
10881105
}
10891106

1107+
/// Sets the current platform's target name for the updater.
1108+
///
1109+
/// By default Tauri looks for a target in the format "{target}-{arch}",
1110+
/// where *target* is one of `darwin`, `linux` and `windows`
1111+
/// and *arch* is one of `i686`, `x86_64`, `aarch64` and `armv7`
1112+
/// based on the running platform. You can change the target name with this function.
1113+
///
1114+
/// # Examples
1115+
///
1116+
/// - Use a macOS Universal binary target name:
1117+
///
1118+
/// ```no_run
1119+
/// let mut builder = tauri::Builder::default();
1120+
/// #[cfg(target_os = "macos")]
1121+
/// {
1122+
/// builder = builder.updater_target("darwin-universal");
1123+
/// }
1124+
/// ```
1125+
///
1126+
/// - Append debug information to the target:
1127+
///
1128+
/// ```no_run
1129+
/// let kind = if cfg!(debug_assertions) { "debug" } else { "release" };
1130+
/// tauri::Builder::default()
1131+
/// .updater_target(format!("{}-{}", tauri::updater::target().unwrap(), kind));
1132+
/// ```
1133+
///
1134+
/// - Use the platform's target triple:
1135+
///
1136+
/// ```no_run
1137+
/// tauri::Builder::default()
1138+
/// .updater_target(tauri::utils::platform::target_triple().unwrap());
1139+
/// ```
1140+
#[cfg(updater)]
1141+
pub fn updater_target<T: Into<String>>(mut self, target: T) -> Self {
1142+
self.updater_settings.target.replace(target.into());
1143+
self
1144+
}
1145+
10901146
/// Builds the application.
10911147
#[allow(clippy::type_complexity)]
10921148
pub fn build<A: Assets>(mut self, context: Context<A>) -> crate::Result<App<R>> {
@@ -1186,6 +1242,8 @@ impl<R: Runtime> Builder<R> {
11861242
clipboard_manager,
11871243
#[cfg(feature = "system-tray")]
11881244
tray_handle: None,
1245+
#[cfg(updater)]
1246+
updater_settings: self.updater_settings,
11891247
},
11901248
};
11911249

core/tauri/src/updater/core.rs

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,7 @@ impl<'a, R: Runtime> UpdateBuilder<'a, R> {
235235
self
236236
}
237237

238-
/// Set the target (os)
239-
/// win32, win64, darwin and linux are currently supported
238+
/// Set the target name. Represents the string that is looked up on the updater API or response JSON.
240239
#[allow(dead_code)]
241240
pub fn target(mut self, target: &str) -> Self {
242241
self.target = Some(target.to_owned());
@@ -266,12 +265,17 @@ impl<'a, R: Runtime> UpdateBuilder<'a, R> {
266265
// If no executable path provided, we use current_exe from tauri_utils
267266
let executable_path = self.executable_path.unwrap_or(current_exe()?);
268267

269-
// Did the target is provided by the config?
270-
// Should be: linux, darwin, win32 or win64
268+
let has_custom_target = self.target.is_some();
271269
let target = self
272270
.target
273-
.or_else(get_updater_target)
271+
.or_else(|| get_updater_target().map(Into::into))
274272
.ok_or(Error::UnsupportedPlatform)?;
273+
let arch = get_updater_arch().ok_or(Error::UnsupportedPlatform)?;
274+
let json_target = if has_custom_target {
275+
target.clone()
276+
} else {
277+
format!("{}-{}", target, arch)
278+
};
275279

276280
// Get the extract_path from the provided executable_path
277281
let extract_path = extract_path_from_executable(&self.app.state::<Env>(), &executable_path);
@@ -292,18 +296,17 @@ impl<'a, R: Runtime> UpdateBuilder<'a, R> {
292296
// Allow fallback if more than 1 urls is provided
293297
let mut last_error: Option<Error> = None;
294298
for url in &self.urls {
295-
// replace {{current_version}} and {{target}} in the provided URL
299+
// replace {{current_version}}, {{target}} and {{arch}} in the provided URL
296300
// this is usefull if we need to query example
297-
// https://releases.myapp.com/update/{{target}}/{{current_version}}
301+
// https://releases.myapp.com/update/{{target}}/{{arch}}/{{current_version}}
298302
// will be transleted into ->
299-
// https://releases.myapp.com/update/darwin/1.0.0
303+
// https://releases.myapp.com/update/darwin/aarch64/1.0.0
300304
// The main objective is if the update URL is defined via the Cargo.toml
301305
// the URL will be generated dynamicly
302-
let fixed_link = str::replace(
303-
&str::replace(url, "{{current_version}}", current_version),
304-
"{{target}}",
305-
&target,
306-
);
306+
let fixed_link = url
307+
.replace("{{current_version}}", current_version)
308+
.replace("{{target}}", &target)
309+
.replace("{{arch}}", arch);
307310

308311
// we want JSON only
309312
let mut headers = HashMap::new();
@@ -335,7 +338,7 @@ impl<'a, R: Runtime> UpdateBuilder<'a, R> {
335338
return Err(Error::UpToDate);
336339
};
337340
// Convert the remote result to our local struct
338-
let built_release = RemoteRelease::from_release(&res.data, &target);
341+
let built_release = RemoteRelease::from_release(&res.data, &json_target);
339342
// make sure all went well and the remote data is compatible
340343
// with what we need locally
341344
match built_release {
@@ -745,23 +748,27 @@ fn copy_files_and_run<R: Read + Seek>(archive_buffer: R, extract_path: &Path) ->
745748
Ok(())
746749
}
747750

748-
/// Returns a target os
749-
/// We do not use a helper function like the target_triple
750-
/// from tauri-utils because this function return `None` if
751-
/// the updater do not support the platform.
752-
///
753-
/// Available target: `linux, darwin, win32, win64`
754-
pub fn get_updater_target() -> Option<String> {
751+
pub(crate) fn get_updater_target() -> Option<&'static str> {
755752
if cfg!(target_os = "linux") {
756-
Some("linux".into())
753+
Some("linux")
757754
} else if cfg!(target_os = "macos") {
758-
Some("darwin".into())
755+
Some("darwin")
759756
} else if cfg!(target_os = "windows") {
760-
if cfg!(target_pointer_width = "32") {
761-
Some("win32".into())
762-
} else {
763-
Some("win64".into())
764-
}
757+
Some("windows")
758+
} else {
759+
None
760+
}
761+
}
762+
763+
pub(crate) fn get_updater_arch() -> Option<&'static str> {
764+
if cfg!(target_arch = "x86") {
765+
Some("i686")
766+
} else if cfg!(target_arch = "x86_64") {
767+
Some("x86_64")
768+
} else if cfg!(target_arch = "arm") {
769+
Some("armv7")
770+
} else if cfg!(target_arch = "aarch64") {
771+
Some("aarch64")
765772
} else {
766773
None
767774
}
@@ -859,15 +866,15 @@ mod test {
859866
"notes": "Test version !",
860867
"pub_date": "2020-06-22T19:25:57Z",
861868
"platforms": {
862-
"darwin": {
869+
"darwin-aarch64": {
863870
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJZVGdpKzJmRWZ0SkRvWS9TdFpqTU9xcm1mUmJSSG5OWVlwSklrWkN1SFpWbmh4SDlBcTU3SXpjbm0xMmRjRkphbkpVeGhGcTdrdzlrWGpGVWZQSWdzPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1MDU3CWZpbGU6L1VzZXJzL3J1bm5lci9ydW5uZXJzLzIuMjYzLjAvd29yay90YXVyaS90YXVyaS90YXVyaS9leGFtcGxlcy9jb21tdW5pY2F0aW9uL3NyYy10YXVyaS90YXJnZXQvZGVidWcvYnVuZGxlL29zeC9hcHAuYXBwLnRhci5negp4ZHFlUkJTVnpGUXdDdEhydTE5TGgvRlVPeVhjTnM5RHdmaGx3c0ZPWjZXWnFwVDRNWEFSbUJTZ1ZkU1IwckJGdmlwSzJPd00zZEZFN2hJOFUvL1FDZz09Cg==",
864871
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.app.tar.gz"
865872
},
866-
"linux": {
873+
"linux-x86_64": {
867874
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOWZSM29hTFNmUEdXMHRoOC81WDFFVVFRaXdWOUdXUUdwT0NlMldqdXkyaWVieXpoUmdZeXBJaXRqSm1YVmczNXdRL1Brc0tHb1NOTzhrL1hadFcxdmdnPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE3MzQzCWZpbGU6L2hvbWUvcnVubmVyL3dvcmsvdGF1cmkvdGF1cmkvdGF1cmkvZXhhbXBsZXMvY29tbXVuaWNhdGlvbi9zcmMtdGF1cmkvdGFyZ2V0L2RlYnVnL2J1bmRsZS9hcHBpbWFnZS9hcHAuQXBwSW1hZ2UudGFyLmd6CmRUTUM2bWxnbEtTbUhOZGtERUtaZnpUMG5qbVo5TGhtZWE1SFNWMk5OOENaVEZHcnAvVW0zc1A2ajJEbWZUbU0yalRHT0FYYjJNVTVHOHdTQlYwQkF3PT0K",
868875
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.AppImage.tar.gz"
869876
},
870-
"win64": {
877+
"windows-x86_64": {
871878
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJHMWlvTzRUSlQzTHJOMm5waWpic0p0VVI2R0hUNGxhQVMxdzBPRndlbGpXQXJJakpTN0toRURtVzBkcm15R0VaNTJuS1lZRWdzMzZsWlNKUVAzZGdJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1NTIzCWZpbGU6RDpcYVx0YXVyaVx0YXVyaVx0YXVyaVxleGFtcGxlc1xjb21tdW5pY2F0aW9uXHNyYy10YXVyaVx0YXJnZXRcZGVidWdcYXBwLng2NC5tc2kuemlwCitXa1lQc3A2MCs1KzEwZnVhOGxyZ2dGMlZqbjBaVUplWEltYUdyZ255eUF6eVF1dldWZzFObStaVEQ3QU1RS1lzcjhDVU4wWFovQ1p1QjJXbW1YZUJ3PT0K",
872879
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.x64.msi.zip"
873880
}
@@ -965,7 +972,7 @@ mod test {
965972
}
966973

967974
#[test]
968-
fn simple_http_updater_raw_json_win64() {
975+
fn simple_http_updater_raw_json_windows_x86_64() {
969976
let _m = mockito::mock("GET", "/")
970977
.with_status(200)
971978
.with_header("content-type", "application/json")
@@ -975,7 +982,7 @@ mod test {
975982
let app = crate::test::mock_app();
976983
let check_update = block!(builder(app.handle())
977984
.current_version("0.0.0")
978-
.target("win64")
985+
.target("windows-x86_64")
979986
.url(mockito::server_url())
980987
.build());
981988

@@ -1013,7 +1020,7 @@ mod test {
10131020

10141021
#[test]
10151022
fn simple_http_updater_without_version() {
1016-
let _m = mockito::mock("GET", "/darwin/1.0.0")
1023+
let _m = mockito::mock("GET", "/darwin-aarch64/1.0.0")
10171024
.with_status(200)
10181025
.with_header("content-type", "application/json")
10191026
.with_body(generate_sample_platform_json(
@@ -1027,7 +1034,7 @@ mod test {
10271034
let check_update = block!(builder(app.handle())
10281035
.current_version("1.0.0")
10291036
.url(format!(
1030-
"{}/darwin/{{{{current_version}}}}",
1037+
"{}/darwin-aarch64/{{{{current_version}}}}",
10311038
mockito::server_url()
10321039
))
10331040
.build());
@@ -1040,7 +1047,7 @@ mod test {
10401047

10411048
#[test]
10421049
fn simple_http_updater_percent_decode() {
1043-
let _m = mockito::mock("GET", "/darwin/1.0.0")
1050+
let _m = mockito::mock("GET", "/darwin-aarch64/1.0.0")
10441051
.with_status(200)
10451052
.with_header("content-type", "application/json")
10461053
.with_body(generate_sample_platform_json(
@@ -1055,7 +1062,7 @@ mod test {
10551062
.current_version("1.0.0")
10561063
.url(
10571064
url::Url::parse(&format!(
1058-
"{}/darwin/{{{{current_version}}}}",
1065+
"{}/darwin-aarch64/{{{{current_version}}}}",
10591066
mockito::server_url()
10601067
))
10611068
.unwrap()
@@ -1072,7 +1079,7 @@ mod test {
10721079
let check_update = block!(builder(app.handle())
10731080
.current_version("1.0.0")
10741081
.urls(&[url::Url::parse(&format!(
1075-
"{}/darwin/{{{{current_version}}}}",
1082+
"{}/darwin-aarch64/{{{{current_version}}}}",
10761083
mockito::server_url()
10771084
))
10781085
.unwrap()
@@ -1087,7 +1094,7 @@ mod test {
10871094

10881095
#[test]
10891096
fn simple_http_updater_with_elevated_task() {
1090-
let _m = mockito::mock("GET", "/win64/1.0.0")
1097+
let _m = mockito::mock("GET", "/windows-x86_64/1.0.0")
10911098
.with_status(200)
10921099
.with_header("content-type", "application/json")
10931100
.with_body(generate_sample_with_elevated_task_platform_json(
@@ -1102,7 +1109,7 @@ mod test {
11021109
let check_update = block!(builder(app.handle())
11031110
.current_version("1.0.0")
11041111
.url(format!(
1105-
"{}/win64/{{{{current_version}}}}",
1112+
"{}/windows-x86_64/{{{{current_version}}}}",
11061113
mockito::server_url()
11071114
))
11081115
.build());
@@ -1115,7 +1122,7 @@ mod test {
11151122

11161123
#[test]
11171124
fn http_updater_uptodate() {
1118-
let _m = mockito::mock("GET", "/darwin/10.0.0")
1125+
let _m = mockito::mock("GET", "/darwin-aarch64/10.0.0")
11191126
.with_status(200)
11201127
.with_header("content-type", "application/json")
11211128
.with_body(generate_sample_platform_json(
@@ -1129,7 +1136,7 @@ mod test {
11291136
let check_update = block!(builder(app.handle())
11301137
.current_version("10.0.0")
11311138
.url(format!(
1132-
"{}/darwin/{{{{current_version}}}}",
1139+
"{}/darwin-aarch64/{{{{current_version}}}}",
11331140
mockito::server_url()
11341141
))
11351142
.build());

0 commit comments

Comments
 (0)