diff --git a/.changes/bundler-object-custom-sign-command.md b/.changes/bundler-object-custom-sign-command.md new file mode 100644 index 000000000..ee0110f78 --- /dev/null +++ b/.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. diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index 90c2831a3..dca49bf04 100644 --- a/core/tauri-config-schema/schema.json +++ b/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: ", "type": "object", diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 2089da339..174990e5b 100644 --- a/core/tauri-utils/src/config.rs +++ b/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, + }, +} + /// Windows bundler configuration. /// /// See more: @@ -935,19 +963,14 @@ pub struct WindowsConfig { /// Configuration for the installer generated with NSIS. pub nsis: Option, /// 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, + pub sign_command: Option, } impl Default for WindowsConfig { diff --git a/tooling/bundler/src/bundle.rs b/tooling/bundler/src/bundle.rs index eb222295e..17100cb9c 100644 --- a/tooling/bundler/src/bundle.rs +++ b/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")] diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 0f910a1ea..da7538b7e 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -459,6 +459,17 @@ pub struct NsisSettings { pub installer_hooks: Option, } +/// 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, +} + /// 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, + pub sign_command: Option, } impl Default for WindowsSettings { diff --git a/tooling/bundler/src/bundle/windows/sign.rs b/tooling/bundler/src/bundle/windows/sign.rs index 7ce214061..229c40f8c 100644 --- a/tooling/bundler/src/bundle/windows/sign.rs +++ b/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, pub tsp: bool, - pub sign_command: Option, + pub sign_command: Option, } #[cfg(windows)] @@ -136,16 +136,14 @@ pub fn verify(path: &Path) -> crate::Result { Ok(cmd.status()?.success()) } -pub fn sign_command_custom>(path: P, command: &str) -> crate::Result { +pub fn sign_command_custom>( + path: P, + command: &CustomSignCommandSettings, +) -> crate::Result { 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>(path: P, params: &SignParams) -> crate::Resu } } -pub fn sign_custom>(path: P, custom_command: &str) -> crate::Result<()> { +pub fn sign_custom>( + 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)); diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 90c2831a3..dca49bf04 100644 --- a/tooling/cli/schema.json +++ b/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: ", "type": "object", diff --git a/tooling/cli/src/helpers/config.rs b/tooling/cli/src/helpers/config.rs index 52a4585b5..2aba580f7 100644 --- a/tooling/cli/src/helpers/config.rs +++ b/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 = OnceLock::new(); CONFIG_HANDLE.get_or_init(Default::default) diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index a2b748323..85ea91965 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/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