mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
feat(bundle): add --no-sign flag to skip code signing in bundling pro… (#14052)
* feat(bundle): add --no-sign flag to skip code signing in bundling process - Introduce a o_sign option in bundle settings to allow skipping code signing - Update macOS and Windows bundler implementations to respect the flag - Wire up CLI option --no-sign to control signing behavior during bundling - Add necessary config and type changes to propagate the flag throughout bundler Signed-off-by: ShigrafS <shigrafsalik@proton.me> * ci: added yml for github action testing Signed-off-by: ShigrafS <shigrafsalik@proton.me> * fix: fixed field 'digest_algorithm' is already declared error Signed-off-by: ShigrafS <shigrafsalik@proton.me> * ci: updated to test the new features as well Signed-off-by: ShigrafS <shigrafsalik@proton.me> * ci: fixed yml issue Signed-off-by: ShigrafS <shigrafsalik@proton.me> * fix: fixed missing parameter issue in android sign.rs Signed-off-by: ShigrafS <shigrafsalik@proton.me> * chore: apply linting Signed-off-by: ShigrafS <shigrafsalik@proton.me> * chore: remove redundant files Signed-off-by: ShigrafS <shigrafsalik@proton.me> * chore: revert indentations Signed-off-by: ShigrafS <shigrafsalik@proton.me> * fix: added parameters to ios mobile build.rs Signed-off-by: ShigrafS <shigrafsalik@proton.me> * docs: updated documentation for settigs.rs Signed-off-by: ShigrafS <shigrafsalik@proton.me> * docs(cli): add documentation for o_sign flag in build options Signed-off-by: ShigrafS <shigrafsalik@proton.me> * chore: apply cargo fmt Signed-off-by: ShigrafS <shigrafsalik@proton.me> * docs: added CHANGES.md Signed-off-by: ShigrafS <shigrafsalik@proton.me> * refactor(bundler): make o_sign private and add getter Signed-off-by: ShigrafS <shigrafsalik@proton.me> * fix: minor error Signed-off-by: ShigrafS <shigrafsalik@proton.me> * refactor: revert build_benchmark_jsons.rs Signed-off-by: ShigrafS <shigrafsalik@proton.me> * impl for macos too * fix ci * fix windows build --------- Signed-off-by: ShigrafS <shigrafsalik@proton.me> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
6
.changes/CHANGES.md
Normal file
6
.changes/CHANGES.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'tauri-cli': 'minor:feat'
|
||||
'tauri-bundler': 'minor:feat'
|
||||
---
|
||||
|
||||
Add a `--no-sign` flag to the `tauri build` and `tauri bundle` commands to skip the code signing step, improving the developer experience for local testing and development without requiring code signing keys.
|
||||
@@ -85,41 +85,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
|
||||
// Sign windows binaries before the bundling step in case neither wix and nsis bundles are enabled
|
||||
if matches!(target_os, TargetPlatform::Windows) {
|
||||
if settings.can_sign() {
|
||||
for bin in settings.binaries() {
|
||||
if bin.main() {
|
||||
// we will sign the main binary after patching per "package type"
|
||||
continue;
|
||||
}
|
||||
let bin_path = settings.binary_path(bin);
|
||||
windows::sign::try_sign(&bin_path, settings)?;
|
||||
}
|
||||
|
||||
// Sign the sidecar binaries
|
||||
for bin in settings.external_binaries() {
|
||||
let path = bin?;
|
||||
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
if windows::sign::verify(&path)? {
|
||||
log::info!(
|
||||
"sidecar at \"{}\" already signed. Skipping...",
|
||||
path.display()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
windows::sign::try_sign(&path, settings)?;
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
|
||||
}
|
||||
}
|
||||
sign_binaries_if_needed(settings, target_os)?;
|
||||
|
||||
let main_binary = settings
|
||||
.binaries()
|
||||
@@ -134,8 +100,9 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
// - codesigning tools should handle calculating+updating this, we just need to ensure
|
||||
// (re)signing is performed after every `patch_binary()` operation
|
||||
// - signing an already-signed binary can result in multiple signatures, causing verification errors
|
||||
let main_binary_reset_required =
|
||||
matches!(target_os, TargetPlatform::Windows) && settings.can_sign() && package_types.len() > 1;
|
||||
let main_binary_reset_required = matches!(target_os, TargetPlatform::Windows)
|
||||
&& settings.windows().can_sign()
|
||||
&& package_types.len() > 1;
|
||||
let mut unsigned_main_binary_copy = tempfile::tempfile()?;
|
||||
if main_binary_reset_required {
|
||||
let mut unsigned_main_binary = std::fs::File::open(&main_binary_path)?;
|
||||
@@ -155,7 +122,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
}
|
||||
|
||||
// sign main binary for every package type after patch
|
||||
if matches!(target_os, TargetPlatform::Windows) && settings.can_sign() {
|
||||
if matches!(target_os, TargetPlatform::Windows) && settings.windows().can_sign() {
|
||||
if main_binary_signed && main_binary_reset_required {
|
||||
let mut signed_main_binary = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -305,6 +272,51 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
|
||||
Ok(bundles)
|
||||
}
|
||||
|
||||
fn sign_binaries_if_needed(settings: &Settings, target_os: &TargetPlatform) -> crate::Result<()> {
|
||||
if matches!(target_os, TargetPlatform::Windows) {
|
||||
if settings.windows().can_sign() {
|
||||
if settings.no_sign() {
|
||||
log::info!("Skipping binary signing due to --no-sign flag.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for bin in settings.binaries() {
|
||||
if bin.main() {
|
||||
// we will sign the main binary after patching per "package type"
|
||||
continue;
|
||||
}
|
||||
let bin_path = settings.binary_path(bin);
|
||||
windows::sign::try_sign(&bin_path, settings)?;
|
||||
}
|
||||
|
||||
// Sign the sidecar binaries
|
||||
for bin in settings.external_binaries() {
|
||||
let path = bin?;
|
||||
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
if windows::sign::verify(&path)? {
|
||||
log::info!(
|
||||
"sidecar at \"{}\" already signed. Skipping...",
|
||||
path.display()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
windows::sign::try_sign(&path, settings)?;
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check to see if there are icons in the settings struct
|
||||
pub fn check_icons(settings: &Settings) -> crate::Result<bool> {
|
||||
// make a peekable iterator of the icon_files
|
||||
|
||||
@@ -103,7 +103,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
||||
|
||||
copy_custom_files_to_bundle(&bundle_directory, settings)?;
|
||||
|
||||
if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? {
|
||||
if settings.no_sign() {
|
||||
log::warn!("Skipping signing due to --no-sign flag.",);
|
||||
} else if let Some(keychain) =
|
||||
super::sign::keychain(settings.macos().signing_identity.as_deref())?
|
||||
{
|
||||
// Sign frameworks and sidecar binaries first, per apple, signing must be done inside out
|
||||
// https://developer.apple.com/forums/thread/701514
|
||||
sign_paths.push(SignTarget {
|
||||
|
||||
@@ -195,7 +195,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
|
||||
// Sign DMG if needed
|
||||
// skipping self-signing DMGs https://github.com/tauri-apps/tauri/issues/12288
|
||||
let identity = settings.macos().signing_identity.as_deref();
|
||||
if identity != Some("-") {
|
||||
if !settings.no_sign() && identity != Some("-") {
|
||||
if let Some(keychain) = super::sign::keychain(identity)? {
|
||||
super::sign::sign(
|
||||
&keychain,
|
||||
|
||||
@@ -572,6 +572,12 @@ pub struct WindowsSettings {
|
||||
pub sign_command: Option<CustomSignCommandSettings>,
|
||||
}
|
||||
|
||||
impl WindowsSettings {
|
||||
pub(crate) fn can_sign(&self) -> bool {
|
||||
self.sign_command.is_some() || self.certificate_thumbprint.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
mod _default {
|
||||
use super::*;
|
||||
@@ -778,6 +784,8 @@ pub struct Settings {
|
||||
target_platform: TargetPlatform,
|
||||
/// The target triple.
|
||||
target: String,
|
||||
/// Whether to disable code signing during the bundling process.
|
||||
no_sign: bool,
|
||||
}
|
||||
|
||||
/// A builder for [`Settings`].
|
||||
@@ -791,6 +799,7 @@ pub struct SettingsBuilder {
|
||||
binaries: Vec<BundleBinary>,
|
||||
target: Option<String>,
|
||||
local_tools_directory: Option<PathBuf>,
|
||||
no_sign: bool,
|
||||
}
|
||||
|
||||
impl SettingsBuilder {
|
||||
@@ -860,6 +869,13 @@ impl SettingsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether to skip code signing.
|
||||
#[must_use]
|
||||
pub fn no_sign(mut self, no_sign: bool) -> Self {
|
||||
self.no_sign = no_sign;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a Settings from the CLI args.
|
||||
///
|
||||
/// Package settings will be read from Cargo.toml.
|
||||
@@ -894,6 +910,7 @@ impl SettingsBuilder {
|
||||
},
|
||||
target_platform,
|
||||
target,
|
||||
no_sign: self.no_sign,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1242,4 +1259,14 @@ impl Settings {
|
||||
pub fn updater(&self) -> Option<&UpdaterSettings> {
|
||||
self.bundle_settings.updater.as_ref()
|
||||
}
|
||||
|
||||
/// Whether to skip signing.
|
||||
pub fn no_sign(&self) -> bool {
|
||||
self.no_sign
|
||||
}
|
||||
|
||||
/// Set whether to skip signing.
|
||||
pub fn set_no_sign(&mut self, no_sign: bool) {
|
||||
self.no_sign = no_sign;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ pub fn build_wix_app_installer(
|
||||
fs::create_dir_all(&output_path)?;
|
||||
|
||||
// when we're performing code signing, we'll sign some WiX DLLs, so we make a local copy
|
||||
let wix_toolset_path = if settings.can_sign() {
|
||||
let wix_toolset_path = if settings.windows().can_sign() {
|
||||
let wix_path = output_path.join("wix");
|
||||
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)
|
||||
.context("failed to copy wix directory")?;
|
||||
@@ -771,7 +771,7 @@ pub fn build_wix_app_installer(
|
||||
let mut extensions = Vec::new();
|
||||
for cap in extension_regex.captures_iter(&fragment) {
|
||||
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
try_sign(&path, settings)?;
|
||||
}
|
||||
extensions.push(path);
|
||||
@@ -785,7 +785,7 @@ pub fn build_wix_app_installer(
|
||||
fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll"));
|
||||
|
||||
// sign default extensions
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
for path in &fragment_extensions {
|
||||
try_sign(path, settings)?;
|
||||
}
|
||||
@@ -879,7 +879,7 @@ pub fn build_wix_app_installer(
|
||||
)?;
|
||||
fs::rename(&msi_output_path, &msi_path)?;
|
||||
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
try_sign(&msi_path, settings)?;
|
||||
}
|
||||
|
||||
@@ -988,7 +988,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
}
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() && should_sign(&resource_path)? {
|
||||
if settings.windows().can_sign() && should_sign(&resource_path)? {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
@@ -1076,7 +1076,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
if !added_resources.iter().any(|r| r.ends_with(&relative_path)) {
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
try_sign(resource_path, settings)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ fn build_nsis_app_installer(
|
||||
|
||||
// we make a copy of the NSIS directory if we're going to sign its DLLs
|
||||
// because we don't want to change the DLL hashes so the cache can reuse it
|
||||
let maybe_plugin_copy_path = if settings.can_sign() {
|
||||
let maybe_plugin_copy_path = if settings.windows().can_sign() {
|
||||
// find nsis path
|
||||
#[cfg(target_os = "linux")]
|
||||
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
|
||||
@@ -283,7 +283,7 @@ fn build_nsis_app_installer(
|
||||
);
|
||||
data.insert("copyright", to_json(settings.copyright_string()));
|
||||
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
|
||||
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
|
||||
}
|
||||
@@ -600,7 +600,7 @@ fn build_nsis_app_installer(
|
||||
));
|
||||
fs::create_dir_all(nsis_installer_path.parent().unwrap())?;
|
||||
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
log::info!("Signing NSIS plugins");
|
||||
for dll in NSIS_PLUGIN_FILES {
|
||||
let path = additional_plugins_path.join(dll);
|
||||
@@ -640,7 +640,7 @@ fn build_nsis_app_installer(
|
||||
|
||||
fs::rename(nsis_output_path, &nsis_installer_path)?;
|
||||
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
try_sign(&nsis_installer_path, settings)?;
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@@ -718,7 +718,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
let loader_path =
|
||||
dunce::simplified(&settings.project_out_directory().join("WebView2Loader.dll")).to_path_buf();
|
||||
if loader_path.exists() {
|
||||
if settings.can_sign() {
|
||||
if settings.windows().can_sign() {
|
||||
try_sign(&loader_path, settings)?;
|
||||
}
|
||||
added_resources.push(loader_path.clone());
|
||||
@@ -743,7 +743,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
|
||||
}
|
||||
added_resources.push(resource_path.clone());
|
||||
|
||||
if settings.can_sign() && should_sign(&resource_path)? {
|
||||
if settings.windows().can_sign() && should_sign(&resource_path)? {
|
||||
try_sign(&resource_path, settings)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,6 @@ use std::sync::OnceLock;
|
||||
use std::{path::Path, process::Command};
|
||||
|
||||
impl Settings {
|
||||
pub(crate) fn can_sign(&self) -> bool {
|
||||
self.windows().sign_command.is_some() || self.windows().certificate_thumbprint.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn sign_params(&self) -> SignParams {
|
||||
SignParams {
|
||||
product_name: self.product_name().into(),
|
||||
@@ -251,7 +247,14 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
}
|
||||
|
||||
pub fn try_sign<P: AsRef<Path>>(file_path: P, settings: &Settings) -> crate::Result<()> {
|
||||
if settings.can_sign() {
|
||||
if settings.no_sign() {
|
||||
log::warn!(
|
||||
"Skipping signing for {} due to --no-sign flag.",
|
||||
tauri_utils::display_path(file_path.as_ref())
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
if settings.windows().can_sign() {
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path.as_ref()));
|
||||
sign(file_path, &settings.sign_params())?;
|
||||
}
|
||||
|
||||
@@ -76,11 +76,18 @@ pub struct Options {
|
||||
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
|
||||
#[clap(long)]
|
||||
pub ignore_version_mismatches: bool,
|
||||
/// Skip code signing when bundling the app
|
||||
#[clap(long)]
|
||||
pub no_sign: bool,
|
||||
}
|
||||
|
||||
pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
|
||||
crate::helpers::app_paths::resolve();
|
||||
|
||||
if options.no_sign {
|
||||
log::warn!("--no-sign flag detected: Signing will be skipped.");
|
||||
}
|
||||
|
||||
let ci = options.ci;
|
||||
|
||||
let target = options
|
||||
|
||||
@@ -92,6 +92,14 @@ pub struct Options {
|
||||
/// On subsequent runs, it's recommended to disable this setting again.
|
||||
#[clap(long)]
|
||||
pub skip_stapling: bool,
|
||||
|
||||
/// Skip code signing during the build or bundling process.
|
||||
///
|
||||
/// Useful for local development and CI environments
|
||||
/// where signing certificates or environment variables
|
||||
/// are not available or not needed.
|
||||
#[clap(long)]
|
||||
pub no_sign: bool,
|
||||
}
|
||||
|
||||
impl From<crate::build::Options> for Options {
|
||||
@@ -104,6 +112,7 @@ impl From<crate::build::Options> for Options {
|
||||
ci: value.ci,
|
||||
config: value.config,
|
||||
skip_stapling: value.skip_stapling,
|
||||
no_sign: value.no_sign,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,6 +206,7 @@ pub fn bundle<A: AppSettings>(
|
||||
let mut settings = app_settings
|
||||
.get_bundler_settings(options.clone().into(), config, out_dir, package_types)
|
||||
.with_context(|| "failed to build bundler settings")?;
|
||||
settings.set_no_sign(options.no_sign);
|
||||
|
||||
settings.set_log_level(match verbosity {
|
||||
0 => log::Level::Error,
|
||||
|
||||
@@ -99,6 +99,7 @@ impl From<Options> for BuildOptions {
|
||||
ci: options.ci,
|
||||
skip_stapling: false,
|
||||
ignore_version_mismatches: options.ignore_version_mismatches,
|
||||
no_sign: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ impl From<Options> for BuildOptions {
|
||||
ci: options.ci,
|
||||
skip_stapling: false,
|
||||
ignore_version_mismatches: options.ignore_version_mismatches,
|
||||
no_sign: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user