@@ -11,6 +11,10 @@ import { PlistSession } from "plist-merge-patch";
1111import { EOL } from "os" ;
1212import * as temp from "temp" ;
1313import * as plist from "plist" ;
14+ import { cert , provision } from "ios-mobileprovision-finder" ;
15+ import { Xcode } from "pbxproj-dom/xcode" ;
16+
17+ type XcodeSigningStyle = "Manual" | "Automatic" ;
1418
1519export class IOSProjectService extends projectServiceBaseLib . PlatformProjectServiceBase implements IPlatformProjectService {
1620 private static XCODE_PROJECT_EXT_NAME = ".xcodeproj" ;
@@ -300,9 +304,62 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
300304
301305 let xcodeBuildVersion = this . getXcodeVersion ( ) ;
302306 if ( helpers . versionCompare ( xcodeBuildVersion , "8.0" ) >= 0 ) {
303- let teamId = this . getDevelopmentTeam ( ) ;
304- if ( teamId ) {
305- args = args . concat ( "DEVELOPMENT_TEAM=" + teamId ) ;
307+ // TRICKY: I am not sure why we totally disregard the buildConfig parameter here.
308+ buildConfig = buildConfig || { } ;
309+
310+ if ( this . $options . teamId ) {
311+ buildConfig . teamIdentifier = this . $options . teamId ;
312+ } else {
313+ buildConfig = this . readXCConfigSigning ( ) ;
314+ if ( ! buildConfig . codeSignIdentity && ! buildConfig . mobileProvisionIdentifier && ! buildConfig . teamIdentifier ) {
315+ buildConfig = this . readBuildConfigFromPlatforms ( ) ;
316+ }
317+ }
318+
319+ let signingStyle : XcodeSigningStyle ;
320+ if ( buildConfig . codeSignIdentity || buildConfig . mobileProvisionIdentifier ) {
321+ signingStyle = "Manual" ;
322+ } else if ( buildConfig . teamIdentifier ) {
323+ signingStyle = "Automatic" ;
324+ } else {
325+ let signingStyles = [
326+ "Manual - Select existing provisioning profile for use" ,
327+ "Automatic - Select Team ID for signing and let Xcode select managed provisioning profile"
328+ ] ;
329+ let signingStyleIndex = signingStyles . indexOf ( this . $prompter . promptForChoice ( "Select codesiging style" , signingStyles ) . wait ( ) ) ;
330+ signingStyle = new Array < XcodeSigningStyle > ( "Manual" , "Automatic" ) [ signingStyleIndex ] ;
331+
332+ switch ( signingStyle ) {
333+ case "Manual" :
334+ let profile = this . getProvisioningProfile ( ) ;
335+ if ( ! profile ) {
336+ this . $logger . error ( "No matching provisioning profile found." ) ;
337+ }
338+ this . persistProvisioningProfiles ( profile . UUID ) ;
339+ this . $logger . info ( "Apply provisioning profile: " + profile . Name + " (" + profile . TeamName + ") " + profile . Type + " UUID: " + profile . UUID ) ;
340+ buildConfig . mobileProvisionIdentifier = profile . UUID ;
341+ buildConfig . teamIdentifier = profile . TeamIdentifier [ 0 ] ;
342+ break ;
343+ case "Automatic" :
344+ buildConfig . teamIdentifier = this . getDevelopmentTeam ( ) ;
345+ this . persistDevelopmentTeam ( buildConfig . teamIdentifier ) ;
346+ break ;
347+ }
348+ }
349+
350+ switch ( signingStyle ) {
351+ case "Manual" : {
352+ const pbxprojPath = path . join ( projectRoot , this . $projectData . projectName + ".xcodeproj" , "project.pbxproj" ) ;
353+ const xcode = Xcode . open ( pbxprojPath ) ;
354+ xcode . setManualSigningStyle ( this . $projectData . projectName ) ;
355+ xcode . save ( ) ;
356+ } break ;
357+ case "Automatic" : {
358+ const pbxprojPath = path . join ( projectRoot , this . $projectData . projectName + ".xcodeproj" , "project.pbxproj" ) ;
359+ const xcode = Xcode . open ( pbxprojPath ) ;
360+ xcode . setAutomaticSigningStyle ( this . $projectData . projectName , buildConfig . teamIdentifier ) ;
361+ xcode . save ( ) ;
362+ } break ;
306363 }
307364 }
308365
@@ -314,6 +371,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
314371 args . push ( `PROVISIONING_PROFILE=${ buildConfig . mobileProvisionIdentifier } ` ) ;
315372 }
316373
374+ if ( buildConfig && buildConfig . teamIdentifier ) {
375+ args . push ( `DEVELOPMENT_TEAM=${ buildConfig . teamIdentifier } ` ) ;
376+ }
377+
317378 this . $childProcess . spawnFromEvent ( "xcodebuild" , args , "exit" , { cwd : this . $options , stdio : 'inherit' } ) . wait ( ) ;
318379 this . createIpa ( projectRoot ) . wait ( ) ;
319380
@@ -1045,73 +1106,148 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10451106 return null ;
10461107 }
10471108
1048- private readTeamId ( ) : string {
1109+ private readXCConfigSigning ( ) : IiOSBuildConfig {
1110+ const result : IiOSBuildConfig = { } ;
10491111 let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
10501112 if ( this . $fs . exists ( xcconfigFile ) . wait ( ) ) {
10511113 let text = this . $fs . readText ( xcconfigFile ) . wait ( ) ;
1052- let teamId : string ;
10531114 text . split ( / \r ? \n / ) . forEach ( ( line ) => {
10541115 line = line . replace ( / \/ ( \/ ) [ ^ \n ] * $ / , "" ) ;
1055- if ( line . indexOf ( "DEVELOPMENT_TEAM" ) >= 0 ) {
1056- teamId = line . split ( "=" ) [ 1 ] . trim ( ) ;
1057- if ( teamId [ teamId . length - 1 ] === ';' ) {
1058- teamId = teamId . slice ( 0 , - 1 ) ;
1116+ const read = ( name : string ) => {
1117+ if ( line . indexOf ( name ) >= 0 ) {
1118+ let value = line . substr ( line . lastIndexOf ( "=" ) + 1 ) . trim ( ) ;
1119+ if ( value . charAt ( value . length - 1 ) === ';' ) {
1120+ value = value . substr ( 0 , value . length - 1 ) . trim ( ) ;
1121+ }
1122+ return value ;
10591123 }
1060- }
1124+ return undefined ;
1125+ } ;
1126+ result . teamIdentifier = read ( "DEVELOPMENT_TEAM" ) || result . teamIdentifier ;
1127+ result . codeSignIdentity = read ( "CODE_SIGN_IDENTITY" ) || result . codeSignIdentity ;
1128+ result . mobileProvisionIdentifier = read ( "PROVISIONING_PROFILE[sdk=iphoneos*]" ) || result . mobileProvisionIdentifier ;
10611129 } ) ;
1062- if ( teamId ) {
1063- return teamId ;
1064- }
10651130 }
1066- let fileName = path . join ( this . platformData . projectRoot , "teamid" ) ;
1067- if ( this . $fs . exists ( fileName ) . wait ( ) ) {
1068- return this . $fs . readText ( fileName ) . wait ( ) ;
1131+ return result ;
1132+ }
1133+
1134+ private getProvisioningProfile ( ) : provision . MobileProvision {
1135+ let profile : provision . MobileProvision ;
1136+
1137+ const allCertificates = cert . read ( ) ;
1138+ const allProfiles = provision . read ( ) ;
1139+ const query : provision . Query = {
1140+ Certificates : allCertificates . valid ,
1141+ AppId : this . $projectData . projectId ,
1142+ Type : "Development"
1143+ } ;
1144+
1145+ if ( this . $options . device ) {
1146+ query . ProvisionedDevices = [ this . $options . device ] ;
1147+ } else {
1148+ this . $devicesService . initialize ( ) . wait ( ) ;
1149+ let deviceUDIDs = _ ( this . $devicesService . getDeviceInstances ( ) )
1150+ . filter ( d => this . $mobileHelper . isiOSPlatform ( d . deviceInfo . platform ) )
1151+ . map ( d => d . deviceInfo . identifier )
1152+ . toJSON ( ) ;
1153+ query . ProvisionedDevices = deviceUDIDs ;
1154+ }
1155+
1156+ const result = provision . select ( allProfiles , query ) ;
1157+ const choiceMap = result . eligable . reduce ( ( acc , p ) => {
1158+ acc [ `'${ p . Name } ' (${ p . TeamName } ) ${ p . Type } ` ] = p ;
1159+ return acc ;
1160+ } , < { [ display : string ] : provision . MobileProvision } > { } ) ;
1161+
1162+ const choices = Object . keys ( choiceMap ) ;
1163+ if ( choices . length > 0 ) {
1164+ const choice = this . $prompter . promptForChoice (
1165+ `Select provisioning profiles (found ${ result . eligable . length } eligable, and ${ result . nonEligable . length } non-eligable)` ,
1166+ choices
1167+ ) . wait ( ) ;
1168+ profile = choiceMap [ choice ] ;
10691169 }
1070- return null ;
1170+
1171+ return profile ;
10711172 }
10721173
10731174 private getDevelopmentTeam ( ) : string {
10741175 let teamId : string ;
1075- if ( this . $options . teamId ) {
1076- teamId = this . $options . teamId ;
1077- } else {
1078- teamId = this . readTeamId ( ) ;
1176+ let teams = this . getDevelopmentTeams ( ) ;
1177+ this . $logger . warn ( "Xcode 8 requires a team id to be specified when building for device." ) ;
1178+ this . $logger . warn ( "You can specify the team id by setting the DEVELOPMENT_TEAM setting in build.xcconfig file located in App_Resources folder of your app, or by using the --teamId option when calling run, debug or livesync commnads." ) ;
1179+ if ( teams . length === 1 ) {
1180+ teamId = teams [ 0 ] . id ;
1181+ this . $logger . warn ( "Found and using the following development team installed on your system: " + teams [ 0 ] . name + " (" + teams [ 0 ] . id + ")" ) ;
1182+ } else if ( teams . length > 0 ) {
1183+ let choices : string [ ] = [ ] ;
1184+ for ( let team of teams ) {
1185+ choices . push ( team . name + " (" + team . id + ")" ) ;
1186+ }
1187+ let choice = this . $prompter . promptForChoice ( 'Found multiple development teams, select one:' , choices ) . wait ( ) ;
1188+ teamId = teams [ choices . indexOf ( choice ) ] . id ;
10791189 }
1080- if ( ! teamId ) {
1081- let teams = this . getDevelopmentTeams ( ) ;
1082- this . $logger . warn ( "Xcode 8 requires a team id to be specified when building for device." ) ;
1083- this . $logger . warn ( "You can specify the team id by setting the DEVELOPMENT_TEAM setting in build.xcconfig file located in App_Resources folder of your app, or by using the --teamId option when calling run, debug or livesync commnads." ) ;
1084- if ( teams . length === 1 ) {
1085- teamId = teams [ 0 ] . id ;
1086- this . $logger . warn ( "Found and using the following development team installed on your system: " + teams [ 0 ] . name + " (" + teams [ 0 ] . id + ")" ) ;
1087- } else if ( teams . length > 0 ) {
1088- let choices : string [ ] = [ ] ;
1089- for ( let team of teams ) {
1090- choices . push ( team . name + " (" + team . id + ")" ) ;
1190+ return teamId ;
1191+ }
1192+
1193+ private persistProvisioningProfiles ( uuid : string ) {
1194+ let choicesPersist = [
1195+ "Yes, set the PROVISIONING_PROFILE[sdk=iphoneos*] setting in build.xcconfig file." ,
1196+ "Yes, persist the mobileprovision uuid in platforms folder." ,
1197+ "No, don't persist this setting."
1198+ ] ;
1199+ let choicePersist = this . $prompter . promptForChoice ( "Do you want to make mobileprovision: " + uuid + " a persistent choice for your app?" , choicesPersist ) . wait ( ) ;
1200+ switch ( choicesPersist . indexOf ( choicePersist ) ) {
1201+ case 0 :
1202+ let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1203+ this . $fs . appendFile ( xcconfigFile , "\nPROVISIONING_PROFILE[sdk=iphoneos*] = " + uuid + "\n" ) . wait ( ) ;
1204+ break ;
1205+ case 1 :
1206+ this . $fs . writeFile ( path . join ( this . platformData . projectRoot , "mobileprovision" ) , uuid ) . wait ( ) ;
1207+ const teamidPath = path . join ( this . platformData . projectRoot , "teamid" ) ;
1208+ if ( this . $fs . exists ( teamidPath ) . wait ( ) ) {
1209+ this . $fs . deleteFile ( teamidPath ) . wait ( ) ;
10911210 }
1092- let choice = this . $prompter . promptForChoice ( 'Found multiple development teams, select one:' , choices ) . wait ( ) ;
1093- teamId = teams [ choices . indexOf ( choice ) ] . id ;
1094-
1095- let choicesPersist = [
1096- "Yes, set the DEVELOPMENT_TEAM setting in build.xcconfig file." ,
1097- "Yes, persist the team id in platforms folder." ,
1098- "No, don't persist this setting."
1099- ] ;
1100- let choicePersist = this . $prompter . promptForChoice ( "Do you want to make teamId: " + teamId + " a persistent choice for your app?" , choicesPersist ) . wait ( ) ;
1101- switch ( choicesPersist . indexOf ( choicePersist ) ) {
1102- case 0 :
1103- let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1104- this . $fs . appendFile ( xcconfigFile , "\nDEVELOPMENT_TEAM = " + teamId + "\n" ) . wait ( ) ;
1105- break ;
1106- case 1 :
1107- this . $fs . writeFile ( path . join ( this . platformData . projectRoot , "teamid" ) , teamId ) ;
1108- break ;
1109- default :
1110- break ;
1211+ break ;
1212+ default :
1213+ break ;
1214+ }
1215+ }
1216+
1217+ private persistDevelopmentTeam ( teamId : string ) {
1218+ let choicesPersist = [
1219+ "Yes, set the DEVELOPMENT_TEAM setting in build.xcconfig file." ,
1220+ "Yes, persist the team id in platforms folder." ,
1221+ "No, don't persist this setting."
1222+ ] ;
1223+ let choicePersist = this . $prompter . promptForChoice ( "Do you want to make teamId: " + teamId + " a persistent choice for your app?" , choicesPersist ) . wait ( ) ;
1224+ switch ( choicesPersist . indexOf ( choicePersist ) ) {
1225+ case 0 :
1226+ let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1227+ this . $fs . appendFile ( xcconfigFile , "\nDEVELOPMENT_TEAM = " + teamId + "\n" ) . wait ( ) ;
1228+ break ;
1229+ case 1 :
1230+ this . $fs . writeFile ( path . join ( this . platformData . projectRoot , "teamid" ) , teamId ) . wait ( ) ;
1231+ const mobileprovisionPath = path . join ( this . platformData . projectRoot , "mobileprovision" ) ;
1232+ if ( this . $fs . exists ( mobileprovisionPath ) . wait ( ) ) {
1233+ this . $fs . deleteFile ( mobileprovisionPath ) . wait ( ) ;
11111234 }
1112- }
1235+ break ;
1236+ default :
1237+ break ;
11131238 }
1114- return teamId ;
1239+ }
1240+
1241+ private readBuildConfigFromPlatforms ( ) : IiOSBuildConfig {
1242+ let mobileprovisionPath = path . join ( this . platformData . projectRoot , "mobileprovision" ) ;
1243+ if ( this . $fs . exists ( mobileprovisionPath ) . wait ( ) ) {
1244+ return { mobileProvisionIdentifier : this . $fs . readText ( mobileprovisionPath ) . wait ( ) } ;
1245+ }
1246+ let teamidPath = path . join ( this . platformData . projectRoot , "teamid" ) ;
1247+ if ( this . $fs . exists ( teamidPath ) . wait ( ) ) {
1248+ return { teamIdentifier : this . $fs . readText ( teamidPath ) . wait ( ) } ;
1249+ }
1250+ return { } ;
11151251 }
11161252}
11171253
0 commit comments