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>
This commit is contained in:
anatawa12 2024-08-20 03:55:58 +09:00 committed by GitHub
parent 5c335ae9ad
commit 8d148a9e25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 162 additions and 31 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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 {

View File

@ -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")]

View File

@ -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 {

View File

@ -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));

View File

@ -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",

View File

@ -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)

View File

@ -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