Bladeren bron

feat: Custom sign command with object notation (#10634)

* feat!: Custom sign command with object notation

* implement JsonSchema if schema feature is enabled

* feat: support old string-based custom signing command config

* format: Run cargo fmt inside tooling/bundler folder

* chore: update json schema

* format: cargo fmt again

* small cleanup

* fix change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
anatawa12 11 maanden geleden
bovenliggende
commit
8d148a9e25

+ 8 - 0
.changes/bundler-object-custom-sign-command.md

@@ -0,0 +1,8 @@
+---
+"tauri-bundler": patch:feat
+"tauri-utils": patch:feat
+"@tauri-apps/cli": patch:feat
+"tauri-cli": patch:feat
+---
+
+Custom sign command with object notation for whitespaces in the command path and arguments.

+ 39 - 4
core/tauri-config-schema/schema.json

@@ -2033,10 +2033,14 @@
           ]
         },
         "signCommand": {
-          "description": "Specify a custom command to sign the binaries.\n This command needs to have a `%1` in it which is just a placeholder for the binary path,\n which we will detect and replace before calling the command.\n\n Example:\n ```text\n sign-cli --arg1 --arg2 %1\n ```\n\n By Default we use `signtool.exe` which can be found only on Windows so\n if you are on another platform and want to cross-compile and sign you will\n need to use another tool like `osslsigncode`.",
-          "type": [
-            "string",
-            "null"
+          "description": "Specify a custom command to sign the binaries.\n This command needs to have a `%1` in args which is just a placeholder for the binary path,\n which we will detect and replace before calling the command.\n\n By Default we use `signtool.exe` which can be found only on Windows so\n if you are on another platform and want to cross-compile and sign you will\n need to use another tool like `osslsigncode`.",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/CustomSignCommandConfig"
+            },
+            {
+              "type": "null"
+            }
           ]
         }
       },
@@ -2425,6 +2429,37 @@
         }
       ]
     },
+    "CustomSignCommandConfig": {
+      "description": "Custom Signing Command configuration.",
+      "anyOf": [
+        {
+          "description": "A string notation of the script to execute.\n\n \"%1\" will be replaced with the path to the binary to be signed.\n\n This is a simpler notation for the command.\n Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.\n\n If you need to use whitespace in the command or arguments, use the object notation [`Self::ScriptWithOptions`].",
+          "type": "string"
+        },
+        {
+          "description": "An object notation of the command.\n\n This is more complex notation for the command but\n this allows you to use whitespace in the command and arguments.",
+          "type": "object",
+          "required": [
+            "args",
+            "cmd"
+          ],
+          "properties": {
+            "cmd": {
+              "description": "The command to run to sign the binary.",
+              "type": "string"
+            },
+            "args": {
+              "description": "The arguments to pass to the command.\n\n \"%1\" will be replaced with the path to the binary to be signed.",
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
     "LinuxConfig": {
       "description": "Configuration for Linux bundles.\n\n See more: <https://tauri.app/v1/api/config#linuxconfig>",
       "type": "object",

+ 30 - 7
core/tauri-utils/src/config.rs

@@ -891,6 +891,34 @@ impl Default for WebviewInstallMode {
   }
 }
 
+/// Custom Signing Command configuration.
+#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
+pub enum CustomSignCommandConfig {
+  /// A string notation of the script to execute.
+  ///
+  /// "%1" will be replaced with the path to the binary to be signed.
+  ///
+  /// This is a simpler notation for the command.
+  /// Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.
+  ///
+  /// If you need to use whitespace in the command or arguments, use the object notation [`Self::ScriptWithOptions`].
+  Command(String),
+  /// An object notation of the command.
+  ///
+  /// This is more complex notation for the command but
+  /// this allows you to use whitespace in the command and arguments.
+  CommandWithOptions {
+    /// The command to run to sign the binary.
+    cmd: String,
+    /// The arguments to pass to the command.
+    ///
+    /// "%1" will be replaced with the path to the binary to be signed.
+    args: Vec<String>,
+  },
+}
+
 /// Windows bundler configuration.
 ///
 /// See more: <https://tauri.app/v1/api/config#windowsconfig>
@@ -935,19 +963,14 @@ pub struct WindowsConfig {
   /// Configuration for the installer generated with NSIS.
   pub nsis: Option<NsisConfig>,
   /// Specify a custom command to sign the binaries.
-  /// This command needs to have a `%1` in it which is just a placeholder for the binary path,
+  /// This command needs to have a `%1` in args which is just a placeholder for the binary path,
   /// which we will detect and replace before calling the command.
   ///
-  /// Example:
-  /// ```text
-  /// sign-cli --arg1 --arg2 %1
-  /// ```
-  ///
   /// By Default we use `signtool.exe` which can be found only on Windows so
   /// if you are on another platform and want to cross-compile and sign you will
   /// need to use another tool like `osslsigncode`.
   #[serde(alias = "sign-command")]
-  pub sign_command: Option<String>,
+  pub sign_command: Option<CustomSignCommandConfig>,
 }
 
 impl Default for WindowsConfig {

+ 3 - 3
tooling/bundler/src/bundle.rs

@@ -20,9 +20,9 @@ use tauri_utils::display_path;
 pub use self::{
   category::AppCategory,
   settings::{
-    AppImageSettings, BundleBinary, BundleSettings, DebianSettings, DmgSettings, MacOsSettings,
-    PackageSettings, PackageType, Position, RpmSettings, Settings, SettingsBuilder, Size,
-    UpdaterSettings,
+    AppImageSettings, BundleBinary, BundleSettings, CustomSignCommandSettings, DebianSettings,
+    DmgSettings, MacOsSettings, PackageSettings, PackageType, Position, RpmSettings, Settings,
+    SettingsBuilder, Size, UpdaterSettings,
   },
 };
 #[cfg(target_os = "macos")]

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

@@ -459,6 +459,17 @@ pub struct NsisSettings {
   pub installer_hooks: Option<PathBuf>,
 }
 
+/// The Custom Signing Command Settings for Windows exe
+#[derive(Clone, Debug)]
+pub struct CustomSignCommandSettings {
+  /// The command to run to sign the binary.
+  pub cmd: String,
+  /// The arguments to pass to the command.
+  ///
+  /// "%1" will be replaced with the path to the binary to be signed.
+  pub args: Vec<String>,
+}
+
 /// The Windows bundle settings.
 #[derive(Clone, Debug)]
 pub struct WindowsSettings {
@@ -504,7 +515,7 @@ pub struct WindowsSettings {
   /// By Default we use `signtool.exe` which can be found only on Windows so
   /// if you are on another platform and want to cross-compile and sign you will
   /// need to use another tool like `osslsigncode`.
-  pub sign_command: Option<String>,
+  pub sign_command: Option<CustomSignCommandSettings>,
 }
 
 impl Default for WindowsSettings {

+ 12 - 11
tooling/bundler/src/bundle/windows/sign.rs

@@ -3,10 +3,10 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
+use crate::bundle::settings::CustomSignCommandSettings;
 #[cfg(windows)]
 use crate::bundle::windows::util;
 use crate::{bundle::common::CommandExt, Settings};
-use anyhow::Context;
 #[cfg(windows)]
 use std::path::PathBuf;
 #[cfg(windows)]
@@ -50,7 +50,7 @@ pub struct SignParams {
   pub certificate_thumbprint: String,
   pub timestamp_url: Option<String>,
   pub tsp: bool,
-  pub sign_command: Option<String>,
+  pub sign_command: Option<CustomSignCommandSettings>,
 }
 
 #[cfg(windows)]
@@ -136,16 +136,14 @@ pub fn verify(path: &Path) -> crate::Result<bool> {
   Ok(cmd.status()?.success())
 }
 
-pub fn sign_command_custom<P: AsRef<Path>>(path: P, command: &str) -> crate::Result<Command> {
+pub fn sign_command_custom<P: AsRef<Path>>(
+  path: P,
+  command: &CustomSignCommandSettings,
+) -> crate::Result<Command> {
   let path = path.as_ref();
 
-  let mut args = command.trim().split(' ');
-  let bin = args
-    .next()
-    .context("custom signing command doesn't contain a bin?")?;
-
-  let mut cmd = Command::new(bin);
-  for arg in args {
+  let mut cmd = Command::new(&command.cmd);
+  for arg in &command.args {
     if arg == "%1" {
       cmd.arg(path);
     } else {
@@ -194,7 +192,10 @@ pub fn sign_command<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Resu
   }
 }
 
-pub fn sign_custom<P: AsRef<Path>>(path: P, custom_command: &str) -> crate::Result<()> {
+pub fn sign_custom<P: AsRef<Path>>(
+  path: P,
+  custom_command: &CustomSignCommandSettings,
+) -> crate::Result<()> {
   let path = path.as_ref();
 
   log::info!(action = "Signing";"{} with a custom signing command", tauri_utils::display_path(path));

+ 39 - 4
tooling/cli/schema.json

@@ -2033,10 +2033,14 @@
           ]
         },
         "signCommand": {
-          "description": "Specify a custom command to sign the binaries.\n This command needs to have a `%1` in it which is just a placeholder for the binary path,\n which we will detect and replace before calling the command.\n\n Example:\n ```text\n sign-cli --arg1 --arg2 %1\n ```\n\n By Default we use `signtool.exe` which can be found only on Windows so\n if you are on another platform and want to cross-compile and sign you will\n need to use another tool like `osslsigncode`.",
-          "type": [
-            "string",
-            "null"
+          "description": "Specify a custom command to sign the binaries.\n This command needs to have a `%1` in args which is just a placeholder for the binary path,\n which we will detect and replace before calling the command.\n\n By Default we use `signtool.exe` which can be found only on Windows so\n if you are on another platform and want to cross-compile and sign you will\n need to use another tool like `osslsigncode`.",
+          "anyOf": [
+            {
+              "$ref": "#/definitions/CustomSignCommandConfig"
+            },
+            {
+              "type": "null"
+            }
           ]
         }
       },
@@ -2425,6 +2429,37 @@
         }
       ]
     },
+    "CustomSignCommandConfig": {
+      "description": "Custom Signing Command configuration.",
+      "anyOf": [
+        {
+          "description": "A string notation of the script to execute.\n\n \"%1\" will be replaced with the path to the binary to be signed.\n\n This is a simpler notation for the command.\n Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments.\n\n If you need to use whitespace in the command or arguments, use the object notation [`Self::ScriptWithOptions`].",
+          "type": "string"
+        },
+        {
+          "description": "An object notation of the command.\n\n This is more complex notation for the command but\n this allows you to use whitespace in the command and arguments.",
+          "type": "object",
+          "required": [
+            "args",
+            "cmd"
+          ],
+          "properties": {
+            "cmd": {
+              "description": "The command to run to sign the binary.",
+              "type": "string"
+            },
+            "args": {
+              "description": "The arguments to pass to the command.\n\n \"%1\" will be replaced with the path to the binary to be signed.",
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            }
+          },
+          "additionalProperties": false
+        }
+      ]
+    },
     "LinuxConfig": {
       "description": "Configuration for Linux bundles.\n\n See more: <https://tauri.app/v1/api/config#linuxconfig>",
       "type": "object",

+ 17 - 0
tooling/cli/src/helpers/config.rs

@@ -110,6 +110,23 @@ pub fn nsis_settings(config: NsisConfig) -> tauri_bundler::NsisSettings {
   }
 }
 
+pub fn custom_sign_settings(
+  config: CustomSignCommandConfig,
+) -> tauri_bundler::CustomSignCommandSettings {
+  match config {
+    CustomSignCommandConfig::Command(command) => {
+      let mut tokens = command.split(' ');
+      tauri_bundler::CustomSignCommandSettings {
+        cmd: tokens.next().unwrap().to_string(), // split always has at least one element
+        args: tokens.map(String::from).collect(),
+      }
+    }
+    CustomSignCommandConfig::CommandWithOptions { cmd, args } => {
+      tauri_bundler::CustomSignCommandSettings { cmd, args }
+    }
+  }
+}
+
 fn config_handle() -> &'static ConfigHandle {
   static CONFIG_HANDLE: OnceLock<ConfigHandle> = OnceLock::new();
   CONFIG_HANDLE.get_or_init(Default::default)

+ 2 - 1
tooling/cli/src/interface/rust.rs

@@ -40,6 +40,7 @@ mod cargo_config;
 mod desktop;
 pub mod installation;
 pub mod manifest;
+use crate::helpers::config::custom_sign_settings;
 use cargo_config::Config as CargoConfig;
 use manifest::{rewrite_manifest, Manifest};
 
@@ -1424,7 +1425,7 @@ fn tauri_config_to_bundle_settings(
       webview_install_mode: config.windows.webview_install_mode,
       webview_fixed_runtime_path: config.windows.webview_fixed_runtime_path,
       allow_downgrades: config.windows.allow_downgrades,
-      sign_command: config.windows.sign_command,
+      sign_command: config.windows.sign_command.map(custom_sign_settings),
     },
     license: config.license.or_else(|| {
       settings