Skip to content

Commit 2a06d10

Browse files
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>
1 parent 5908972 commit 2a06d10

File tree

12 files changed

+128
-57
lines changed

12 files changed

+128
-57
lines changed

.changes/CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-cli': 'minor:feat'
3+
'tauri-bundler': 'minor:feat'
4+
---
5+
6+
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.

crates/tauri-bundler/src/bundle.rs

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -85,41 +85,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
8585
}
8686

8787
// Sign windows binaries before the bundling step in case neither wix and nsis bundles are enabled
88-
if matches!(target_os, TargetPlatform::Windows) {
89-
if settings.can_sign() {
90-
for bin in settings.binaries() {
91-
if bin.main() {
92-
// we will sign the main binary after patching per "package type"
93-
continue;
94-
}
95-
let bin_path = settings.binary_path(bin);
96-
windows::sign::try_sign(&bin_path, settings)?;
97-
}
98-
99-
// Sign the sidecar binaries
100-
for bin in settings.external_binaries() {
101-
let path = bin?;
102-
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
103-
if skip {
104-
continue;
105-
}
106-
107-
#[cfg(windows)]
108-
if windows::sign::verify(&path)? {
109-
log::info!(
110-
"sidecar at \"{}\" already signed. Skipping...",
111-
path.display()
112-
);
113-
continue;
114-
}
115-
116-
windows::sign::try_sign(&path, settings)?;
117-
}
118-
} else {
119-
#[cfg(not(target_os = "windows"))]
120-
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...");
121-
}
122-
}
88+
sign_binaries_if_needed(settings, target_os)?;
12389

12490
let main_binary = settings
12591
.binaries()
@@ -134,8 +100,9 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
134100
// - codesigning tools should handle calculating+updating this, we just need to ensure
135101
// (re)signing is performed after every `patch_binary()` operation
136102
// - signing an already-signed binary can result in multiple signatures, causing verification errors
137-
let main_binary_reset_required =
138-
matches!(target_os, TargetPlatform::Windows) && settings.can_sign() && package_types.len() > 1;
103+
let main_binary_reset_required = matches!(target_os, TargetPlatform::Windows)
104+
&& settings.windows().can_sign()
105+
&& package_types.len() > 1;
139106
let mut unsigned_main_binary_copy = tempfile::tempfile()?;
140107
if main_binary_reset_required {
141108
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>> {
155122
}
156123

157124
// sign main binary for every package type after patch
158-
if matches!(target_os, TargetPlatform::Windows) && settings.can_sign() {
125+
if matches!(target_os, TargetPlatform::Windows) && settings.windows().can_sign() {
159126
if main_binary_signed && main_binary_reset_required {
160127
let mut signed_main_binary = std::fs::OpenOptions::new()
161128
.write(true)
@@ -305,6 +272,51 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
305272
Ok(bundles)
306273
}
307274

275+
fn sign_binaries_if_needed(settings: &Settings, target_os: &TargetPlatform) -> crate::Result<()> {
276+
if matches!(target_os, TargetPlatform::Windows) {
277+
if settings.windows().can_sign() {
278+
if settings.no_sign() {
279+
log::info!("Skipping binary signing due to --no-sign flag.");
280+
return Ok(());
281+
}
282+
283+
for bin in settings.binaries() {
284+
if bin.main() {
285+
// we will sign the main binary after patching per "package type"
286+
continue;
287+
}
288+
let bin_path = settings.binary_path(bin);
289+
windows::sign::try_sign(&bin_path, settings)?;
290+
}
291+
292+
// Sign the sidecar binaries
293+
for bin in settings.external_binaries() {
294+
let path = bin?;
295+
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
296+
if skip {
297+
continue;
298+
}
299+
300+
#[cfg(windows)]
301+
if windows::sign::verify(&path)? {
302+
log::info!(
303+
"sidecar at \"{}\" already signed. Skipping...",
304+
path.display()
305+
);
306+
continue;
307+
}
308+
309+
windows::sign::try_sign(&path, settings)?;
310+
}
311+
} else {
312+
#[cfg(not(target_os = "windows"))]
313+
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...");
314+
}
315+
}
316+
317+
Ok(())
318+
}
319+
308320
/// Check to see if there are icons in the settings struct
309321
pub fn check_icons(settings: &Settings) -> crate::Result<bool> {
310322
// make a peekable iterator of the icon_files

crates/tauri-bundler/src/bundle/macos/app.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,11 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
103103

104104
copy_custom_files_to_bundle(&bundle_directory, settings)?;
105105

106-
if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? {
106+
if settings.no_sign() {
107+
log::warn!("Skipping signing due to --no-sign flag.",);
108+
} else if let Some(keychain) =
109+
super::sign::keychain(settings.macos().signing_identity.as_deref())?
110+
{
107111
// Sign frameworks and sidecar binaries first, per apple, signing must be done inside out
108112
// https://developer.apple.com/forums/thread/701514
109113
sign_paths.push(SignTarget {

crates/tauri-bundler/src/bundle/macos/dmg/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
195195
// Sign DMG if needed
196196
// skipping self-signing DMGs https://github.com/tauri-apps/tauri/issues/12288
197197
let identity = settings.macos().signing_identity.as_deref();
198-
if identity != Some("-") {
198+
if !settings.no_sign() && identity != Some("-") {
199199
if let Some(keychain) = super::sign::keychain(identity)? {
200200
super::sign::sign(
201201
&keychain,

crates/tauri-bundler/src/bundle/settings.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,12 @@ pub struct WindowsSettings {
572572
pub sign_command: Option<CustomSignCommandSettings>,
573573
}
574574

575+
impl WindowsSettings {
576+
pub(crate) fn can_sign(&self) -> bool {
577+
self.sign_command.is_some() || self.certificate_thumbprint.is_some()
578+
}
579+
}
580+
575581
#[allow(deprecated)]
576582
mod _default {
577583
use super::*;
@@ -778,6 +784,8 @@ pub struct Settings {
778784
target_platform: TargetPlatform,
779785
/// The target triple.
780786
target: String,
787+
/// Whether to disable code signing during the bundling process.
788+
no_sign: bool,
781789
}
782790

783791
/// A builder for [`Settings`].
@@ -791,6 +799,7 @@ pub struct SettingsBuilder {
791799
binaries: Vec<BundleBinary>,
792800
target: Option<String>,
793801
local_tools_directory: Option<PathBuf>,
802+
no_sign: bool,
794803
}
795804

796805
impl SettingsBuilder {
@@ -860,6 +869,13 @@ impl SettingsBuilder {
860869
self
861870
}
862871

872+
/// Sets whether to skip code signing.
873+
#[must_use]
874+
pub fn no_sign(mut self, no_sign: bool) -> Self {
875+
self.no_sign = no_sign;
876+
self
877+
}
878+
863879
/// Builds a Settings from the CLI args.
864880
///
865881
/// Package settings will be read from Cargo.toml.
@@ -894,6 +910,7 @@ impl SettingsBuilder {
894910
},
895911
target_platform,
896912
target,
913+
no_sign: self.no_sign,
897914
})
898915
}
899916
}
@@ -1242,4 +1259,14 @@ impl Settings {
12421259
pub fn updater(&self) -> Option<&UpdaterSettings> {
12431260
self.bundle_settings.updater.as_ref()
12441261
}
1262+
1263+
/// Whether to skip signing.
1264+
pub fn no_sign(&self) -> bool {
1265+
self.no_sign
1266+
}
1267+
1268+
/// Set whether to skip signing.
1269+
pub fn set_no_sign(&mut self, no_sign: bool) {
1270+
self.no_sign = no_sign;
1271+
}
12451272
}

crates/tauri-bundler/src/bundle/windows/msi/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ pub fn build_wix_app_installer(
470470
fs::create_dir_all(&output_path)?;
471471

472472
// when we're performing code signing, we'll sign some WiX DLLs, so we make a local copy
473-
let wix_toolset_path = if settings.can_sign() {
473+
let wix_toolset_path = if settings.windows().can_sign() {
474474
let wix_path = output_path.join("wix");
475475
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)
476476
.context("failed to copy wix directory")?;
@@ -771,7 +771,7 @@ pub fn build_wix_app_installer(
771771
let mut extensions = Vec::new();
772772
for cap in extension_regex.captures_iter(&fragment) {
773773
let path = wix_toolset_path.join(format!("Wix{}.dll", &cap[1]));
774-
if settings.can_sign() {
774+
if settings.windows().can_sign() {
775775
try_sign(&path, settings)?;
776776
}
777777
extensions.push(path);
@@ -785,7 +785,7 @@ pub fn build_wix_app_installer(
785785
fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll"));
786786

787787
// sign default extensions
788-
if settings.can_sign() {
788+
if settings.windows().can_sign() {
789789
for path in &fragment_extensions {
790790
try_sign(path, settings)?;
791791
}
@@ -879,7 +879,7 @@ pub fn build_wix_app_installer(
879879
)?;
880880
fs::rename(&msi_output_path, &msi_path)?;
881881

882-
if settings.can_sign() {
882+
if settings.windows().can_sign() {
883883
try_sign(&msi_path, settings)?;
884884
}
885885

@@ -988,7 +988,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
988988
}
989989
added_resources.push(resource_path.clone());
990990

991-
if settings.can_sign() && should_sign(&resource_path)? {
991+
if settings.windows().can_sign() && should_sign(&resource_path)? {
992992
try_sign(&resource_path, settings)?;
993993
}
994994

@@ -1076,7 +1076,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
10761076
.to_string_lossy()
10771077
.into_owned();
10781078
if !added_resources.iter().any(|r| r.ends_with(&relative_path)) {
1079-
if settings.can_sign() {
1079+
if settings.windows().can_sign() {
10801080
try_sign(resource_path, settings)?;
10811081
}
10821082

crates/tauri-bundler/src/bundle/windows/nsis/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ fn build_nsis_app_installer(
192192

193193
// we make a copy of the NSIS directory if we're going to sign its DLLs
194194
// because we don't want to change the DLL hashes so the cache can reuse it
195-
let maybe_plugin_copy_path = if settings.can_sign() {
195+
let maybe_plugin_copy_path = if settings.windows().can_sign() {
196196
// find nsis path
197197
#[cfg(target_os = "linux")]
198198
let system_nsis_toolset_path = std::env::var_os("NSIS_PATH")
@@ -283,7 +283,7 @@ fn build_nsis_app_installer(
283283
);
284284
data.insert("copyright", to_json(settings.copyright_string()));
285285

286-
if settings.can_sign() {
286+
if settings.windows().can_sign() {
287287
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
288288
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
289289
}
@@ -600,7 +600,7 @@ fn build_nsis_app_installer(
600600
));
601601
fs::create_dir_all(nsis_installer_path.parent().unwrap())?;
602602

603-
if settings.can_sign() {
603+
if settings.windows().can_sign() {
604604
log::info!("Signing NSIS plugins");
605605
for dll in NSIS_PLUGIN_FILES {
606606
let path = additional_plugins_path.join(dll);
@@ -640,7 +640,7 @@ fn build_nsis_app_installer(
640640

641641
fs::rename(nsis_output_path, &nsis_installer_path)?;
642642

643-
if settings.can_sign() {
643+
if settings.windows().can_sign() {
644644
try_sign(&nsis_installer_path, settings)?;
645645
} else {
646646
#[cfg(not(target_os = "windows"))]
@@ -718,7 +718,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
718718
let loader_path =
719719
dunce::simplified(&settings.project_out_directory().join("WebView2Loader.dll")).to_path_buf();
720720
if loader_path.exists() {
721-
if settings.can_sign() {
721+
if settings.windows().can_sign() {
722722
try_sign(&loader_path, settings)?;
723723
}
724724
added_resources.push(loader_path.clone());
@@ -743,7 +743,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
743743
}
744744
added_resources.push(resource_path.clone());
745745

746-
if settings.can_sign() && should_sign(&resource_path)? {
746+
if settings.windows().can_sign() && should_sign(&resource_path)? {
747747
try_sign(&resource_path, settings)?;
748748
}
749749

crates/tauri-bundler/src/bundle/windows/sign.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ use std::sync::OnceLock;
1414
use std::{path::Path, process::Command};
1515

1616
impl Settings {
17-
pub(crate) fn can_sign(&self) -> bool {
18-
self.windows().sign_command.is_some() || self.windows().certificate_thumbprint.is_some()
19-
}
20-
2117
pub(crate) fn sign_params(&self) -> SignParams {
2218
SignParams {
2319
product_name: self.product_name().into(),
@@ -251,7 +247,14 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
251247
}
252248

253249
pub fn try_sign<P: AsRef<Path>>(file_path: P, settings: &Settings) -> crate::Result<()> {
254-
if settings.can_sign() {
250+
if settings.no_sign() {
251+
log::warn!(
252+
"Skipping signing for {} due to --no-sign flag.",
253+
tauri_utils::display_path(file_path.as_ref())
254+
);
255+
return Ok(());
256+
}
257+
if settings.windows().can_sign() {
255258
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path.as_ref()));
256259
sign(file_path, &settings.sign_params())?;
257260
}

crates/tauri-cli/src/build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,18 @@ pub struct Options {
7676
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
7777
#[clap(long)]
7878
pub ignore_version_mismatches: bool,
79+
/// Skip code signing when bundling the app
80+
#[clap(long)]
81+
pub no_sign: bool,
7982
}
8083

8184
pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
8285
crate::helpers::app_paths::resolve();
8386

87+
if options.no_sign {
88+
log::warn!("--no-sign flag detected: Signing will be skipped.");
89+
}
90+
8491
let ci = options.ci;
8592

8693
let target = options

crates/tauri-cli/src/bundle.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ pub struct Options {
9292
/// On subsequent runs, it's recommended to disable this setting again.
9393
#[clap(long)]
9494
pub skip_stapling: bool,
95+
96+
/// Skip code signing during the build or bundling process.
97+
///
98+
/// Useful for local development and CI environments
99+
/// where signing certificates or environment variables
100+
/// are not available or not needed.
101+
#[clap(long)]
102+
pub no_sign: bool,
95103
}
96104

97105
impl From<crate::build::Options> for Options {
@@ -104,6 +112,7 @@ impl From<crate::build::Options> for Options {
104112
ci: value.ci,
105113
config: value.config,
106114
skip_stapling: value.skip_stapling,
115+
no_sign: value.no_sign,
107116
}
108117
}
109118
}
@@ -197,6 +206,7 @@ pub fn bundle<A: AppSettings>(
197206
let mut settings = app_settings
198207
.get_bundler_settings(options.clone().into(), config, out_dir, package_types)
199208
.with_context(|| "failed to build bundler settings")?;
209+
settings.set_no_sign(options.no_sign);
200210

201211
settings.set_log_level(match verbosity {
202212
0 => log::Level::Error,

0 commit comments

Comments
 (0)