|
@@ -19,6 +19,8 @@ use semver::Version;
|
|
|
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
|
|
|
use tauri_utils::{platform::current_exe, Env};
|
|
|
use time::OffsetDateTime;
|
|
|
+#[cfg(feature = "tracing")]
|
|
|
+use tracing::Instrument;
|
|
|
use url::Url;
|
|
|
|
|
|
#[cfg(desktop)]
|
|
@@ -312,6 +314,10 @@ impl<R: Runtime> UpdateBuilder<R> {
|
|
|
Ok(self)
|
|
|
}
|
|
|
|
|
|
+ #[cfg_attr(
|
|
|
+ feature = "tracing",
|
|
|
+ tracing::instrument("updater::check", skip_all, fields(arch, target), ret, err)
|
|
|
+ )]
|
|
|
pub async fn build(mut self) -> Result<Update<R>> {
|
|
|
let mut remote_release: Option<RemoteRelease> = None;
|
|
|
|
|
@@ -335,6 +341,12 @@ impl<R: Runtime> UpdateBuilder<R> {
|
|
|
(target.to_string(), format!("{target}-{arch}"))
|
|
|
};
|
|
|
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ {
|
|
|
+ tracing::Span::current().record("arch", arch);
|
|
|
+ tracing::Span::current().record("target", &target);
|
|
|
+ }
|
|
|
+
|
|
|
// Get the extract_path from the provided executable_path
|
|
|
let extract_path = extract_path_from_executable(&self.app.state::<Env>(), &executable_path);
|
|
|
|
|
@@ -370,38 +382,75 @@ impl<R: Runtime> UpdateBuilder<R> {
|
|
|
.replace("{{target}}", &target)
|
|
|
.replace("{{arch}}", arch);
|
|
|
|
|
|
- let mut request = HttpRequestBuilder::new("GET", &fixed_link)?.headers(headers.clone());
|
|
|
- if let Some(timeout) = self.timeout {
|
|
|
- request = request.timeout(timeout);
|
|
|
- }
|
|
|
- let resp = ClientBuilder::new().build()?.send(request).await;
|
|
|
-
|
|
|
- // If we got a success, we stop the loop
|
|
|
- // and we set our remote_release variable
|
|
|
- if let Ok(res) = resp {
|
|
|
- let status = res.status();
|
|
|
- // got status code 2XX
|
|
|
- if status.is_success() {
|
|
|
- // if we got 204
|
|
|
- if status == StatusCode::NO_CONTENT {
|
|
|
- // return with `UpToDate` error
|
|
|
- // we should catch on the client
|
|
|
- return Err(Error::UpToDate);
|
|
|
- };
|
|
|
- let res = res.read().await?;
|
|
|
- // Convert the remote result to our local struct
|
|
|
- let built_release = serde_json::from_value(res.data).map_err(Into::into);
|
|
|
- // make sure all went well and the remote data is compatible
|
|
|
- // with what we need locally
|
|
|
- match built_release {
|
|
|
- Ok(release) => {
|
|
|
- last_error = None;
|
|
|
- remote_release = Some(release);
|
|
|
- break;
|
|
|
+ let task = async {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::debug!("checking if there is an update via {}", url);
|
|
|
+
|
|
|
+ let mut request = HttpRequestBuilder::new("GET", &fixed_link)?.headers(headers.clone());
|
|
|
+ if let Some(timeout) = self.timeout {
|
|
|
+ request = request.timeout(timeout);
|
|
|
+ }
|
|
|
+ let resp = ClientBuilder::new().build()?.send(request).await;
|
|
|
+
|
|
|
+ // If we got a success, we stop the loop
|
|
|
+ // and we set our remote_release variable
|
|
|
+ if let Ok(res) = resp {
|
|
|
+ let status = res.status();
|
|
|
+ // got status code 2XX
|
|
|
+ if status.is_success() {
|
|
|
+ // if we got 204
|
|
|
+ if status == StatusCode::NO_CONTENT {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::event!(tracing::Level::DEBUG, kind = "result", data = "no content");
|
|
|
+ // return with `UpToDate` error
|
|
|
+ // we should catch on the client
|
|
|
+ return Err(Error::UpToDate);
|
|
|
+ };
|
|
|
+ let res = res.read().await?;
|
|
|
+
|
|
|
+ // Convert the remote result to our local struct
|
|
|
+ let built_release: Result<RemoteRelease> =
|
|
|
+ serde_json::from_value(res.data).map_err(Into::into);
|
|
|
+
|
|
|
+ // make sure all went well and the remote data is compatible
|
|
|
+ // with what we need locally
|
|
|
+ match built_release {
|
|
|
+ Ok(release) => {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::event!(
|
|
|
+ tracing::Level::DEBUG,
|
|
|
+ kind = "result",
|
|
|
+ data = tracing::field::debug(&release)
|
|
|
+ );
|
|
|
+ last_error = None;
|
|
|
+ return Ok(Some(release));
|
|
|
+ }
|
|
|
+ Err(err) => {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::event!(
|
|
|
+ tracing::Level::ERROR,
|
|
|
+ kind = "error",
|
|
|
+ error = err.to_string()
|
|
|
+ );
|
|
|
+ last_error = Some(err)
|
|
|
+ }
|
|
|
}
|
|
|
- Err(err) => last_error = Some(err),
|
|
|
- }
|
|
|
- } // if status code is not 2XX we keep loopin' our urls
|
|
|
+ } // if status code is not 2XX we keep loopin' our urls
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(None)
|
|
|
+ };
|
|
|
+
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ let found_release = {
|
|
|
+ let span = tracing::info_span!("updater::check::fetch", url = &fixed_link,);
|
|
|
+ task.instrument(span).await?
|
|
|
+ };
|
|
|
+ #[cfg(not(feature = "tracing"))]
|
|
|
+ let found_release = task.await?;
|
|
|
+ if let Some(release) = found_release {
|
|
|
+ remote_release.replace(release);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -447,7 +496,6 @@ pub(crate) fn builder<R: Runtime>(app: AppHandle<R>) -> UpdateBuilder<R> {
|
|
|
UpdateBuilder::new(app)
|
|
|
}
|
|
|
|
|
|
-#[derive(Debug)]
|
|
|
pub(crate) struct Update<R: Runtime> {
|
|
|
/// Application handle.
|
|
|
pub app: AppHandle<R>,
|
|
@@ -480,6 +528,29 @@ pub(crate) struct Update<R: Runtime> {
|
|
|
headers: HeaderMap,
|
|
|
}
|
|
|
|
|
|
+impl<R: Runtime> fmt::Debug for Update<R> {
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
+ let mut s = f.debug_struct("Update");
|
|
|
+
|
|
|
+ s.field("current_version", &self.current_version)
|
|
|
+ .field("version", &self.version)
|
|
|
+ .field("date", &self.date)
|
|
|
+ .field("should_update", &self.should_update)
|
|
|
+ .field("body", &self.body)
|
|
|
+ .field("target", &self.target)
|
|
|
+ .field("extract_path", &self.extract_path)
|
|
|
+ .field("download_url", &self.download_url)
|
|
|
+ .field("signature", &self.signature)
|
|
|
+ .field("timeout", &self.timeout)
|
|
|
+ .field("headers", &self.headers);
|
|
|
+
|
|
|
+ #[cfg(target_os = "windows")]
|
|
|
+ s.field("with_elevated_task", &self.with_elevated_task);
|
|
|
+
|
|
|
+ s.finish()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
impl<R: Runtime> Clone for Update<R> {
|
|
|
fn clone(&self) -> Self {
|
|
|
Self {
|
|
@@ -527,6 +598,7 @@ impl<R: Runtime> Update<R> {
|
|
|
|
|
|
// Download and install our update
|
|
|
// @todo(lemarier): Split into download and install (two step) but need to be thread safe
|
|
|
+ #[cfg_attr(feature = "tracing", tracing::instrument("updater::download_and_install", skip_all, fields(url = %self.download_url), ret, err))]
|
|
|
pub async fn download_and_install<C: Fn(usize, Option<u64>), D: FnOnce()>(
|
|
|
&self,
|
|
|
pub_key: String,
|
|
@@ -540,6 +612,10 @@ impl<R: Runtime> Update<R> {
|
|
|
// anything with it yet
|
|
|
#[cfg(target_os = "linux")]
|
|
|
if self.app.state::<Env>().appimage.is_none() {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::error!(
|
|
|
+ "app is not a supported Linux package. Currently only AppImages are supported"
|
|
|
+ );
|
|
|
return Err(Error::UnsupportedLinuxPackage);
|
|
|
}
|
|
|
|
|
@@ -561,10 +637,14 @@ impl<R: Runtime> Update<R> {
|
|
|
req = req.timeout(timeout);
|
|
|
}
|
|
|
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::info!("Downloading update");
|
|
|
let response = client.send(req).await?;
|
|
|
|
|
|
// make sure it's success
|
|
|
if !response.status().is_success() {
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::error!("Failed to download update");
|
|
|
return Err(Error::Network(format!(
|
|
|
"Download request failed with status: {}",
|
|
|
response.status()
|
|
@@ -577,17 +657,31 @@ impl<R: Runtime> Update<R> {
|
|
|
.and_then(|value| value.to_str().ok())
|
|
|
.and_then(|value| value.parse().ok());
|
|
|
|
|
|
- let mut buffer = Vec::new();
|
|
|
- {
|
|
|
+ let buffer = {
|
|
|
use futures_util::StreamExt;
|
|
|
let mut stream = response.bytes_stream();
|
|
|
- while let Some(chunk) = stream.next().await {
|
|
|
- let chunk = chunk?;
|
|
|
- let bytes = chunk.as_ref().to_vec();
|
|
|
- on_chunk(bytes.len(), content_length);
|
|
|
- buffer.extend(bytes);
|
|
|
+
|
|
|
+ let task = async move {
|
|
|
+ let mut buffer = Vec::new();
|
|
|
+ while let Some(chunk) = stream.next().await {
|
|
|
+ let chunk = chunk?;
|
|
|
+ let bytes = chunk.as_ref().to_vec();
|
|
|
+ on_chunk(bytes.len(), content_length);
|
|
|
+ buffer.extend(bytes);
|
|
|
+ }
|
|
|
+ Result::Ok(buffer)
|
|
|
+ };
|
|
|
+
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ {
|
|
|
+ let span = tracing::info_span!("updater::download_and_install::stream");
|
|
|
+ task.instrument(span).await
|
|
|
}
|
|
|
- }
|
|
|
+ #[cfg(not(feature = "tracing"))]
|
|
|
+ {
|
|
|
+ task.await
|
|
|
+ }
|
|
|
+ }?;
|
|
|
|
|
|
on_download_finish();
|
|
|
|
|
@@ -601,6 +695,8 @@ impl<R: Runtime> Update<R> {
|
|
|
// TODO: implement updater in mobile
|
|
|
#[cfg(desktop)]
|
|
|
{
|
|
|
+ #[cfg(feature = "tracing")]
|
|
|
+ tracing::info_span!("updater::download_and_install::install");
|
|
|
// we copy the files depending of the operating system
|
|
|
// we run the setup, appimage re-install or overwrite the
|
|
|
// macos .app
|