Переглянути джерело

docs(config): enhance documentation for bundle targets, closes #3251 (#4418)

Lucas Fernandes Nogueira 3 роки тому
батько
коміт
31c15cd2bd

+ 6 - 0
.changes/check-updater-target-cli.md

@@ -0,0 +1,6 @@
+---
+"cli.rs": patch
+"cli.js": patch
+---
+
+Warn if updater is enabled but not in the bundle target list.

+ 5 - 0
.changes/config-targets.md

@@ -0,0 +1,5 @@
+---
+"tauri-utils": patch
+---
+
+Changed the `BundleConfig::targets` to a `BundleTarget` enum to enhance generated documentation.

+ 167 - 13
core/tauri-utils/src/config.rs

@@ -65,23 +65,176 @@ impl Default for WindowUrl {
   }
 }
 
-/// Targets to bundle.
-#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+/// A bundle referenced by tauri-bundler.
+#[derive(Debug, PartialEq, Eq, Clone)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
-#[serde(untagged)]
+#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))]
+pub enum BundleType {
+  /// The debian bundle (.deb).
+  Deb,
+  /// The AppImage bundle (.appimage).
+  AppImage,
+  /// The Microsoft Installer bundle (.msi).
+  Msi,
+  /// The macOS application bundle (.app).
+  App,
+  /// The Apple Disk Image bundle (.dmg).
+  Dmg,
+  /// The Tauri updater bundle.
+  Updater,
+}
+
+impl Display for BundleType {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    write!(
+      f,
+      "{}",
+      match self {
+        Self::Deb => "deb",
+        Self::AppImage => "appimage",
+        Self::Msi => "msi",
+        Self::App => "app",
+        Self::Dmg => "dmg",
+        Self::Updater => "updater",
+      }
+    )
+  }
+}
+
+impl Serialize for BundleType {
+  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+  where
+    S: Serializer,
+  {
+    serializer.serialize_str(self.to_string().as_ref())
+  }
+}
+
+impl<'de> Deserialize<'de> for BundleType {
+  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+  where
+    D: Deserializer<'de>,
+  {
+    let s = String::deserialize(deserializer)?;
+    match s.to_lowercase().as_str() {
+      "deb" => Ok(Self::Deb),
+      "appimage" => Ok(Self::AppImage),
+      "msi" => Ok(Self::Msi),
+      "app" => Ok(Self::App),
+      "dmg" => Ok(Self::Dmg),
+      "updater" => Ok(Self::Updater),
+      _ => Err(DeError::custom(format!("unknown bundle target '{}'", s))),
+    }
+  }
+}
+
+/// Targets to bundle. Each value is case insensitive.
+#[derive(Debug, PartialEq, Eq, Clone)]
 pub enum BundleTarget {
+  /// Bundle all targets.
+  All,
   /// A list of bundle targets.
-  All(Vec<String>),
+  List(Vec<BundleType>),
   /// A single bundle target.
-  One(String),
+  One(BundleType),
+}
+
+#[cfg(feature = "schema")]
+impl schemars::JsonSchema for BundleTarget {
+  fn schema_name() -> std::string::String {
+    "BundleTarget".to_owned()
+  }
+
+  fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+    let any_of = vec![
+      schemars::schema::SchemaObject {
+        enum_values: Some(vec!["all".into()]),
+        metadata: Some(Box::new(schemars::schema::Metadata {
+          description: Some("Bundle all targets.".to_owned()),
+          ..Default::default()
+        })),
+        ..Default::default()
+      }
+      .into(),
+      schemars::_private::apply_metadata(
+        gen.subschema_for::<Vec<BundleType>>(),
+        schemars::schema::Metadata {
+          description: Some("A list of bundle targets.".to_owned()),
+          ..Default::default()
+        },
+      ),
+      schemars::_private::apply_metadata(
+        gen.subschema_for::<BundleType>(),
+        schemars::schema::Metadata {
+          description: Some("A single bundle target.".to_owned()),
+          ..Default::default()
+        },
+      ),
+    ];
+
+    schemars::schema::SchemaObject {
+      subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
+        any_of: Some(any_of),
+        ..Default::default()
+      })),
+      metadata: Some(Box::new(schemars::schema::Metadata {
+        description: Some("Targets to bundle. Each value is case insensitive.".to_owned()),
+        ..Default::default()
+      })),
+      ..Default::default()
+    }
+    .into()
+  }
+}
+
+impl Default for BundleTarget {
+  fn default() -> Self {
+    Self::All
+  }
+}
+
+impl Serialize for BundleTarget {
+  fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+  where
+    S: Serializer,
+  {
+    match self {
+      Self::All => serializer.serialize_str("all"),
+      Self::List(l) => l.serialize(serializer),
+      Self::One(t) => serializer.serialize_str(t.to_string().as_ref()),
+    }
+  }
+}
+
+impl<'de> Deserialize<'de> for BundleTarget {
+  fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+  where
+    D: Deserializer<'de>,
+  {
+    #[derive(Deserialize, Serialize)]
+    #[serde(untagged)]
+    pub enum BundleTargetInner {
+      List(Vec<BundleType>),
+      One(BundleType),
+      All(String),
+    }
+
+    match BundleTargetInner::deserialize(deserializer)? {
+      BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
+      BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {}", t))),
+      BundleTargetInner::List(l) => Ok(Self::List(l)),
+      BundleTargetInner::One(t) => Ok(Self::One(t)),
+    }
+  }
 }
 
 impl BundleTarget {
-  /// Gets the bundle targets as a [`Vec`].
+  /// Gets the bundle targets as a [`Vec`]. The vector is empty when set to [`BundleTarget::All`].
   #[allow(dead_code)]
-  pub fn to_vec(&self) -> Vec<String> {
+  pub fn to_vec(&self) -> Vec<BundleType> {
     match self {
-      Self::All(list) => list.clone(),
+      Self::All => vec![],
+      Self::List(list) => list.clone(),
       Self::One(i) => vec![i.clone()],
     }
   }
@@ -308,11 +461,12 @@ fn default_allow_downgrades() -> bool {
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
 #[serde(rename_all = "camelCase", deny_unknown_fields)]
 pub struct BundleConfig {
-  /// Whether we should build your app with tauri-bundler or plain `cargo build`
+  /// Whether Tauri should bundle your application or just output the executable.
   #[serde(default)]
   pub active: bool,
-  /// The bundle targets, currently supports ["deb", "app", "msi", "appimage", "dmg"] or "all"
-  pub targets: Option<BundleTarget>,
+  /// The bundle targets, currently supports ["deb", "appimage", "msi", "app", "dmg", "updater"] or "all".
+  #[serde(default)]
+  pub targets: BundleTarget,
   /// The application identifier in reverse domain name notation (e.g. `com.tauri.example`).
   /// This string must be unique across applications since it is used in system configurations like
   /// the bundle ID and path to the webview data directory.
@@ -2740,7 +2894,7 @@ mod build {
       let identifier = str_lit(&self.identifier);
       let icon = vec_lit(&self.icon, str_lit);
       let active = self.active;
-      let targets = quote!(None);
+      let targets = quote!(Default::default());
       let resources = quote!(None);
       let copyright = quote!(None);
       let category = quote!(None);
@@ -3159,7 +3313,7 @@ mod test {
       windows: vec![],
       bundle: BundleConfig {
         active: false,
-        targets: None,
+        targets: Default::default(),
         identifier: String::from(""),
         icon: Vec::new(),
         resources: None,

+ 17 - 1
tooling/bundler/src/bundle/settings.rs

@@ -4,7 +4,10 @@
 
 use super::category::AppCategory;
 use crate::bundle::{common, platform::target_triple};
-use tauri_utils::resources::{external_binaries, ResourcePaths};
+use tauri_utils::{
+  config::BundleType,
+  resources::{external_binaries, ResourcePaths},
+};
 
 use std::{
   collections::HashMap,
@@ -33,6 +36,19 @@ pub enum PackageType {
   Updater,
 }
 
+impl From<BundleType> for PackageType {
+  fn from(bundle: BundleType) -> Self {
+    match bundle {
+      BundleType::Deb => Self::Deb,
+      BundleType::AppImage => Self::AppImage,
+      BundleType::Msi => Self::WindowsMsi,
+      BundleType::App => Self::MacOsBundle,
+      BundleType::Dmg => Self::Dmg,
+      BundleType::Updater => Self::Updater,
+    }
+  }
+}
+
 impl PackageType {
   /// Maps a short name to a PackageType.
   /// Possible values are "deb", "ios", "msi", "app", "rpm", "appimage", "dmg", "updater".

+ 31 - 9
tooling/cli/schema.json

@@ -129,6 +129,7 @@
           "macOS": {
             "minimumSystemVersion": "10.13"
           },
+          "targets": "all",
           "windows": {
             "allowDowngrades": true,
             "certificateThumbprint": null,
@@ -259,6 +260,7 @@
             "macOS": {
               "minimumSystemVersion": "10.13"
             },
+            "targets": "all",
             "windows": {
               "allowDowngrades": true,
               "certificateThumbprint": null,
@@ -896,18 +898,16 @@
       ],
       "properties": {
         "active": {
-          "description": "Whether we should build your app with tauri-bundler or plain `cargo build`",
+          "description": "Whether Tauri should bundle your application or just output the executable.",
           "default": false,
           "type": "boolean"
         },
         "targets": {
-          "description": "The bundle targets, currently supports [\"deb\", \"app\", \"msi\", \"appimage\", \"dmg\"] or \"all\"",
-          "anyOf": [
+          "description": "The bundle targets, currently supports [\"deb\", \"appimage\", \"msi\", \"app\", \"dmg\", \"updater\"] or \"all\".",
+          "default": "all",
+          "allOf": [
             {
               "$ref": "#/definitions/BundleTarget"
-            },
-            {
-              "type": "null"
             }
           ]
         },
@@ -1025,21 +1025,43 @@
       "additionalProperties": false
     },
     "BundleTarget": {
-      "description": "Targets to bundle.",
+      "description": "Targets to bundle. Each value is case insensitive.",
       "anyOf": [
+        {
+          "description": "Bundle all targets.",
+          "enum": [
+            "all"
+          ]
+        },
         {
           "description": "A list of bundle targets.",
           "type": "array",
           "items": {
-            "type": "string"
+            "$ref": "#/definitions/BundleType"
           }
         },
         {
           "description": "A single bundle target.",
-          "type": "string"
+          "allOf": [
+            {
+              "$ref": "#/definitions/BundleType"
+            }
+          ]
         }
       ]
     },
+    "BundleType": {
+      "description": "A bundle referenced by tauri-bundler.",
+      "type": "string",
+      "enum": [
+        "deb",
+        "appimage",
+        "msi",
+        "app",
+        "dmg",
+        "updater"
+      ]
+    },
     "AppImageConfig": {
       "description": "Configuration for AppImage bundles.",
       "type": "object",

+ 19 - 22
tooling/cli/src/build.rs

@@ -14,6 +14,7 @@ use anyhow::{bail, Context};
 use clap::Parser;
 #[cfg(target_os = "linux")]
 use heck::ToKebabCase;
+use log::warn;
 use log::{error, info};
 use std::{env::set_current_dir, fs::rename, path::PathBuf, process::Command};
 use tauri_bundler::bundle::{bundle_project, PackageType};
@@ -28,14 +29,20 @@ pub struct Options {
   #[clap(short, long)]
   debug: bool,
   /// Target triple to build against.
+  ///
   /// It must be one of the values outputted by `$rustc --print target-list` or `universal-apple-darwin` for an universal macOS application.
+  ///
   /// Note that compiling an universal macOS application requires both `aarch64-apple-darwin` and `x86_64-apple-darwin` targets to be installed.
   #[clap(short, long)]
   target: Option<String>,
   /// Space or comma separated list of features to activate
   #[clap(short, long, multiple_occurrences(true), multiple_values(true))]
   features: Option<Vec<String>>,
-  /// Space or comma separated list of bundles to package
+  /// Space or comma separated list of bundles to package.
+  ///
+  /// Each bundle must be one of `deb`, `appimage`, `msi`, `app` or `dmg` on MacOS and `updater` on all platforms.
+  ///
+  /// Note that the `updater` bundle is not automatically added so you must specify it if the updater is enabled.
   #[clap(short, long, multiple_occurrences(true), multiple_values(true))]
   bundles: Option<Vec<String>>,
   /// JSON string or path to JSON file to merge with tauri.conf.json
@@ -271,31 +278,21 @@ pub fn command(options: Options) -> Result<()> {
         }
       }
       Some(types)
-    } else if let Some(targets) = &config_.tauri.bundle.targets {
-      let mut types = vec![];
-      let targets = targets.to_vec();
-      if !targets.contains(&"all".into()) {
-        for name in targets {
-          match PackageType::from_short_name(&name) {
-            Some(package_type) => {
-              types.push(package_type);
-            }
-            None => {
-              return Err(anyhow::anyhow!(format!(
-                "Unsupported bundle format: {}",
-                name
-              )));
-            }
-          }
-        }
-        Some(types)
-      } else {
+    } else {
+      let targets = config_.tauri.bundle.targets.to_vec();
+      if targets.is_empty() {
         None
+      } else {
+        Some(targets.into_iter().map(Into::into).collect())
       }
-    } else {
-      None
     };
 
+    if let Some(types) = &package_types {
+      if config_.tauri.updater.active && !types.contains(&PackageType::Updater) {
+        warn!("The updater is enabled but the bundle target list does not contain `updater`, so the updater artifacts won't be generated.");
+      }
+    }
+
     let mut enabled_features = features.clone();
     if !no_default_features {
       enabled_features.push("default".into());