diff --git a/.changes/infer-signing-identity.md b/.changes/infer-signing-identity.md new file mode 100644 index 000000000..c07a6b54d --- /dev/null +++ b/.changes/infer-signing-identity.md @@ -0,0 +1,7 @@ +--- +"tauri-bundler": patch:enhance +"tauri-cli": patch:enhance +"@tauri-apps/cli": patch:enhance +--- + +Infer macOS codesign identity from the `APPLE_CERTIFICATE` environment variable when provided, meaning the identity no longer needs to be provided when signing on CI using that option. If the imported certificate name does not match a provided signingIdentity configuration, an error is returned. diff --git a/tooling/bundler/src/bundle/macos/app.rs b/tooling/bundler/src/bundle/macos/app.rs index 0a7c0a0f4..b91e43c5d 100644 --- a/tooling/bundler/src/bundle/macos/app.rs +++ b/tooling/bundler/src/bundle/macos/app.rs @@ -105,7 +105,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { copy_custom_files_to_bundle(&bundle_directory, settings)?; - if let Some(identity) = &settings.macos().signing_identity { + 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 { @@ -118,7 +118,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { remove_extra_attr(&app_bundle_path)?; // sign application - let keychain = sign(sign_paths, identity, settings)?; + sign(&keychain, sign_paths, settings)?; // notarization is required for distribution match notarize_auth() { diff --git a/tooling/bundler/src/bundle/macos/dmg.rs b/tooling/bundler/src/bundle/macos/dmg.rs index 338866fe7..6e2c0b087 100644 --- a/tooling/bundler/src/bundle/macos/dmg.rs +++ b/tooling/bundler/src/bundle/macos/dmg.rs @@ -186,13 +186,14 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result< fs::rename(bundle_dir.join(dmg_name), dmg_path.clone())?; // Sign DMG if needed - if let Some(identity) = &settings.macos().signing_identity { + + if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? { super::sign::sign( + &keychain, vec![super::sign::SignTarget { path: dmg_path.clone(), is_an_executable: false, }], - identity, settings, )?; } diff --git a/tooling/bundler/src/bundle/macos/sign.rs b/tooling/bundler/src/bundle/macos/sign.rs index d9e82f157..d5bdca52a 100644 --- a/tooling/bundler/src/bundle/macos/sign.rs +++ b/tooling/bundler/src/bundle/macos/sign.rs @@ -16,25 +16,36 @@ pub struct SignTarget { pub is_an_executable: bool, } -pub fn sign( - targets: Vec, - identity: &str, - settings: &Settings, -) -> crate::Result { - log::info!(action = "Signing"; "with identity \"{}\"", identity); - - let keychain = if let (Some(certificate_encoded), Some(certificate_password)) = ( +pub fn keychain(identity: Option<&str>) -> crate::Result> { + if let (Some(certificate_encoded), Some(certificate_password)) = ( var_os("APPLE_CERTIFICATE"), var_os("APPLE_CERTIFICATE_PASSWORD"), ) { - // setup keychain allow you to import your certificate - // for CI build - tauri_macos_sign::Keychain::with_certificate(&certificate_encoded, &certificate_password)? + // import user certificate - useful for for CI build + let keychain = + tauri_macos_sign::Keychain::with_certificate(&certificate_encoded, &certificate_password)?; + if let Some(identity) = identity { + let certificate_identity = keychain.signing_identity(); + if !certificate_identity.contains(identity) { + return Err(anyhow::anyhow!("certificate from APPLE_CERTIFICATE \"{certificate_identity}\" environment variable does not match provided identity \"{identity}\"").into()); + } + } + Ok(Some(keychain)) + } else if let Some(identity) = identity { + Ok(Some(tauri_macos_sign::Keychain::with_signing_identity( + identity, + ))) } else { - tauri_macos_sign::Keychain::with_signing_identity(identity) - }; + Ok(None) + } +} - log::info!("Signing app bundle..."); +pub fn sign( + keychain: &tauri_macos_sign::Keychain, + targets: Vec, + settings: &Settings, +) -> crate::Result<()> { + log::info!(action = "Signing"; "with identity \"{}\"", keychain.signing_identity()); for target in targets { keychain.sign( @@ -44,7 +55,7 @@ pub fn sign( )?; } - Ok(keychain) + Ok(()) } pub fn notarize( diff --git a/tooling/cli/ENVIRONMENT_VARIABLES.md b/tooling/cli/ENVIRONMENT_VARIABLES.md index c10126972..b4e5bb4f7 100644 --- a/tooling/cli/ENVIRONMENT_VARIABLES.md +++ b/tooling/cli/ENVIRONMENT_VARIABLES.md @@ -31,9 +31,9 @@ These environment variables are inputs to the CLI which may have an equivalent C - `API_PRIVATE_KEYS_DIR` — Specify the directory where your AuthKey file is located. See `APPLE_API_KEY`. - `APPLE_API_ISSUER` — Issuer ID. Required if `APPLE_API_KEY` is specified. - `APPLE_API_KEY_PATH` - path to the API key `.p8` file. If not specified, the bundler searches the following directories in sequence for a private key file with the name of 'AuthKey\_.p8': './private_keys', '~/private_keys', '~/.private_keys', and '~/.appstoreconnect/private_keys'. -- `APPLE_SIGNING_IDENTITY` — The identity used to code sign. Overwrites `tauri.conf.json > bundle > macOS > signingIdentity`. +- `APPLE_SIGNING_IDENTITY` — The identity used to code sign. Overwrites `tauri.conf.json > bundle > macOS > signingIdentity`. If neither are set, it is inferred from `APPLE_CERTIFICATE` when provided. - `APPLE_PROVIDER_SHORT_NAME` — If your Apple ID is connected to multiple teams, you have to specify the provider short name of the team you want to use to notarize your app. Overwrites `tauri.conf.json > bundle > macOS > providerShortName`. -- `APPLE_DEVELOPMENT_TEAM` — TODO +- `APPLE_DEVELOPMENT_TEAM` — The team ID used to code sign on iOS. Overwrites `tauri.conf.json > bundle > iOS > developmentTeam`. Can be found in https://developer.apple.com/account#MembershipDetailsCard. - `TAURI_WEBVIEW_AUTOMATION` — Enables webview automation (Linux Only). - `TAURI_ANDROID_PROJECT_PATH` — Path of the tauri android project, usually will be `/src-tauri/gen/android`. - `TAURI_IOS_PROJECT_PATH` — Path of the tauri iOS project, usually will be `/src-tauri/gen/ios`.