33// SPDX-License-Identifier: Apache-2.0
44// SPDX-License-Identifier: MIT
55
6- use std:: ffi:: OsString ;
7- use std:: { fs:: File , io:: prelude:: * , path:: PathBuf , process:: Command } ;
6+ use std:: {
7+ env:: { var, var_os} ,
8+ ffi:: OsString ,
9+ fs:: File ,
10+ io:: prelude:: * ,
11+ path:: PathBuf ,
12+ process:: Command ,
13+ } ;
814
915use crate :: { bundle:: common:: CommandExt , Settings } ;
1016use anyhow:: Context ;
1117use log:: info;
12- use regex :: Regex ;
18+ use serde :: Deserialize ;
1319
1420const KEYCHAIN_ID : & str = "tauri-build.keychain" ;
1521const KEYCHAIN_PWD : & str = "tauri-build" ;
@@ -147,8 +153,8 @@ pub fn sign(
147153 info ! ( action = "Signing" ; "{} with identity \" {}\" " , path_to_sign. display( ) , identity) ;
148154
149155 let setup_keychain = if let ( Some ( certificate_encoded) , Some ( certificate_password) ) = (
150- std :: env :: var_os ( "APPLE_CERTIFICATE" ) ,
151- std :: env :: var_os ( "APPLE_CERTIFICATE_PASSWORD" ) ,
156+ var_os ( "APPLE_CERTIFICATE" ) ,
157+ var_os ( "APPLE_CERTIFICATE_PASSWORD" ) ,
152158 ) {
153159 // setup keychain allow you to import your certificate
154160 // for CI build
@@ -212,13 +218,18 @@ fn try_sign(
212218 Ok ( ( ) )
213219}
214220
221+ #[ derive( Deserialize ) ]
222+ struct NotarytoolSubmitOutput {
223+ id : String ,
224+ status : String ,
225+ message : String ,
226+ }
227+
215228pub fn notarize (
216229 app_bundle_path : PathBuf ,
217- auth_args : Vec < String > ,
230+ auth : NotarizeAuth ,
218231 settings : & Settings ,
219232) -> crate :: Result < ( ) > {
220- let identifier = settings. bundle_identifier ( ) ;
221-
222233 let bundle_stem = app_bundle_path
223234 . file_stem ( )
224235 . expect ( "failed to get bundle filename" ) ;
@@ -252,55 +263,47 @@ pub fn notarize(
252263 sign ( zip_path. clone ( ) , identity, settings, false ) ?;
253264 } ;
254265
255- let mut notarize_args = vec ! [
256- "altool" ,
257- "--notarize-app" ,
258- "-f" ,
266+ let notarize_args = vec ! [
267+ "notarytool" ,
268+ "submit" ,
259269 zip_path
260270 . to_str( )
261271 . expect( "failed to convert zip_path to string" ) ,
262- "--primary-bundle-id" ,
263- identifier,
272+ "--wait" ,
273+ "--output-format" ,
274+ "json" ,
264275 ] ;
265276
266- if let Some ( provider_short_name) = & settings. macos ( ) . provider_short_name {
267- notarize_args. push ( "--asc-provider" ) ;
268- notarize_args. push ( provider_short_name) ;
269- }
270-
271277 info ! ( action = "Notarizing" ; "{}" , app_bundle_path. display( ) ) ;
272278
273279 let output = Command :: new ( "xcrun" )
274280 . args ( notarize_args)
275- . args ( auth_args . clone ( ) )
281+ . notarytool_args ( & auth )
276282 . output_ok ( )
277283 . context ( "failed to upload app to Apple's notarization servers." ) ?;
278284
279- // combine both stdout and stderr to support macOS below 10.15
280- let mut notarize_response = std:: str:: from_utf8 ( & output. stdout ) ?. to_string ( ) ;
281- notarize_response. push ( '\n' ) ;
282- notarize_response. push_str ( std:: str:: from_utf8 ( & output. stderr ) ?) ;
283- notarize_response. push ( '\n' ) ;
284- if let Some ( uuid) = Regex :: new ( r"\nRequestUUID = (.+?)\n" ) ?
285- . captures_iter ( & notarize_response)
286- . next ( )
287- {
288- info ! ( "notarization started; waiting for Apple response..." ) ;
289-
290- let uuid = uuid[ 1 ] . to_string ( ) ;
291- get_notarization_status ( uuid, auth_args) ?;
292- staple_app ( app_bundle_path. clone ( ) ) ?;
285+ if !output. status . success ( ) {
286+ return Err ( anyhow:: anyhow!( "failed to notarize app" ) . into ( ) ) ;
287+ }
288+
289+ let output_str = String :: from_utf8_lossy ( & output. stdout ) ;
290+ if let Ok ( submit_output) = serde_json:: from_str :: < NotarytoolSubmitOutput > ( & output_str) {
291+ let log_message = format ! (
292+ "Finished with status {} for id {} ({})" ,
293+ submit_output. status, submit_output. id, submit_output. message
294+ ) ;
295+ if submit_output. status == "Accepted" {
296+ log:: info!( action = "Notarizing" ; "{}" , log_message) ;
297+ staple_app ( app_bundle_path) ?;
298+ Ok ( ( ) )
299+ } else {
300+ Err ( anyhow:: anyhow!( "{log_message}" ) . into ( ) )
301+ }
293302 } else {
294303 return Err (
295- anyhow:: anyhow!(
296- "failed to parse RequestUUID from upload output. {}" ,
297- notarize_response
298- )
299- . into ( ) ,
304+ anyhow:: anyhow!( "failed to parse notarytool output as JSON: `{output_str}`" ) . into ( ) ,
300305 ) ;
301306 }
302-
303- Ok ( ( ) )
304307}
305308
306309fn staple_app ( mut app_bundle_path : PathBuf ) -> crate :: Result < ( ) > {
@@ -322,82 +325,66 @@ fn staple_app(mut app_bundle_path: PathBuf) -> crate::Result<()> {
322325 Ok ( ( ) )
323326}
324327
325- fn get_notarization_status ( uuid : String , auth_args : Vec < String > ) -> crate :: Result < ( ) > {
326- std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 10 ) ) ;
327- let result = Command :: new ( "xcrun" )
328- . args ( vec ! [ "altool" , "--notarization-info" , & uuid] )
329- . args ( auth_args. clone ( ) )
330- . output_ok ( ) ;
328+ pub enum NotarizeAuth {
329+ AppleId {
330+ apple_id : String ,
331+ password : String ,
332+ } ,
333+ ApiKey {
334+ key : String ,
335+ key_path : PathBuf ,
336+ issuer : String ,
337+ } ,
338+ }
331339
332- if let Ok ( output) = result {
333- // combine both stdout and stderr to support macOS below 10.15
334- let mut notarize_status = std:: str:: from_utf8 ( & output. stdout ) ?. to_string ( ) ;
335- notarize_status. push ( '\n' ) ;
336- notarize_status. push_str ( std:: str:: from_utf8 ( & output. stderr ) ?) ;
337- notarize_status. push ( '\n' ) ;
338- if let Some ( status) = Regex :: new ( r"\n *Status: (.+?)\n" ) ?
339- . captures_iter ( & notarize_status)
340- . next ( )
341- {
342- let status = status[ 1 ] . to_string ( ) ;
343- if status == "in progress" {
344- get_notarization_status ( uuid, auth_args)
345- } else if status == "invalid" {
346- Err (
347- anyhow:: anyhow!( format!(
348- "Apple failed to notarize your app. {}" ,
349- notarize_status
350- ) )
351- . into ( ) ,
352- )
353- } else if status != "success" {
354- Err (
355- anyhow:: anyhow!( format!(
356- "Unknown notarize status {}. {}" ,
357- status, notarize_status
358- ) )
359- . into ( ) ,
360- )
361- } else {
362- Ok ( ( ) )
363- }
364- } else {
365- get_notarization_status ( uuid, auth_args)
340+ pub trait NotarytoolCmdExt {
341+ fn notarytool_args ( & mut self , auth : & NotarizeAuth ) -> & mut Self ;
342+ }
343+
344+ impl NotarytoolCmdExt for Command {
345+ fn notarytool_args ( & mut self , auth : & NotarizeAuth ) -> & mut Self {
346+ match auth {
347+ NotarizeAuth :: AppleId { apple_id, password } => self
348+ . arg ( "--apple-id" )
349+ . arg ( apple_id)
350+ . arg ( "--password" )
351+ . arg ( password) ,
352+ NotarizeAuth :: ApiKey {
353+ key,
354+ key_path,
355+ issuer,
356+ } => self
357+ . arg ( "--key-id" )
358+ . arg ( key)
359+ . arg ( "--key" )
360+ . arg ( key_path)
361+ . arg ( "--issuer" )
362+ . arg ( issuer) ,
366363 }
367- } else {
368- get_notarization_status ( uuid, auth_args)
369364 }
370365}
371366
372- pub fn notarize_auth_args ( ) -> crate :: Result < Vec < String > > {
373- match (
374- std:: env:: var_os ( "APPLE_ID" ) ,
375- std:: env:: var_os ( "APPLE_PASSWORD" ) ,
376- ) {
367+ pub fn notarize_auth ( ) -> crate :: Result < NotarizeAuth > {
368+ match ( var_os ( "APPLE_ID" ) , var_os ( "APPLE_PASSWORD" ) ) {
377369 ( Some ( apple_id) , Some ( apple_password) ) => {
378370 let apple_id = apple_id
379371 . to_str ( )
380372 . expect ( "failed to convert APPLE_ID to string" )
381373 . to_string ( ) ;
382- let apple_password = apple_password
374+ let password = apple_password
383375 . to_str ( )
384376 . expect ( "failed to convert APPLE_PASSWORD to string" )
385377 . to_string ( ) ;
386- Ok ( vec ! [
387- "-u" . to_string( ) ,
388- apple_id,
389- "-p" . to_string( ) ,
390- apple_password,
391- ] )
378+ Ok ( NotarizeAuth :: AppleId { apple_id, password } )
392379 }
393380 _ => {
394- match ( std :: env :: var_os ( "APPLE_API_KEY" ) , std :: env :: var_os ( "APPLE_API_ISSUER" ) ) {
395- ( Some ( api_key) , Some ( api_issuer) ) => {
396- let api_key = api_key. to_str ( ) . expect ( "failed to convert APPLE_API_KEY to string" ) . to_string ( ) ;
397- let api_issuer = api_issuer. to_str ( ) . expect ( "failed to convert APPLE_API_ISSUER to string" ) . to_string ( ) ;
398- Ok ( vec ! [ "--apiKey" . to_string ( ) , api_key , "--apiIssuer" . to_string ( ) , api_issuer ] )
381+ match ( var_os ( "APPLE_API_KEY" ) , var_os ( "APPLE_API_ISSUER" ) , var ( "APPLE_API_KEY_PATH ") ) {
382+ ( Some ( api_key) , Some ( api_issuer) , Ok ( key_path ) ) => {
383+ let key = api_key. to_str ( ) . expect ( "failed to convert APPLE_API_KEY to string" ) . to_string ( ) ;
384+ let issuer = api_issuer. to_str ( ) . expect ( "failed to convert APPLE_API_ISSUER to string" ) . to_string ( ) ;
385+ Ok ( NotarizeAuth :: ApiKey { key , key_path : key_path . into ( ) , issuer } )
399386 } ,
400- _ => Err ( anyhow:: anyhow!( "no APPLE_ID & APPLE_PASSWORD or APPLE_API_KEY & APPLE_API_ISSUER environment variables found" ) . into ( ) )
387+ _ => Err ( anyhow:: anyhow!( "no APPLE_ID & APPLE_PASSWORD or APPLE_API_KEY & APPLE_API_ISSUER & APPLE_API_KEY_PATH environment variables found" ) . into ( ) )
401388 }
402389 }
403390 }
0 commit comments