@@ -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,10 +304,7 @@ 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 ) ;
306- }
307+ buildConfig = this . getBuildConfig ( projectRoot , buildConfig ) . wait ( ) ;
307308 }
308309
309310 if ( buildConfig && buildConfig . codeSignIdentity ) {
@@ -314,6 +315,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
314315 args . push ( `PROVISIONING_PROFILE=${ buildConfig . mobileProvisionIdentifier } ` ) ;
315316 }
316317
318+ if ( buildConfig && buildConfig . teamIdentifier ) {
319+ args . push ( `DEVELOPMENT_TEAM=${ buildConfig . teamIdentifier } ` ) ;
320+ }
321+
317322 this . $childProcess . spawnFromEvent ( "xcodebuild" , args , "exit" , { cwd : this . $options , stdio : 'inherit' } ) . wait ( ) ;
318323 this . createIpa ( projectRoot ) . wait ( ) ;
319324
@@ -1010,6 +1015,69 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10101015 return xcodeBuildVersion ;
10111016 }
10121017
1018+ private getBuildConfig ( projectRoot : string , buildConfig : IiOSBuildConfig ) : IFuture < IiOSBuildConfig > {
1019+ return ( ( ) => {
1020+ // TRICKY: I am not sure why we totally disregard the buildConfig parameter here.
1021+ buildConfig = buildConfig || { } ;
1022+
1023+ if ( this . $options . teamId ) {
1024+ buildConfig . teamIdentifier = this . $options . teamId ;
1025+ } else {
1026+ buildConfig = this . readXCConfigSigning ( ) . wait ( ) ;
1027+ if ( ! buildConfig . codeSignIdentity && ! buildConfig . mobileProvisionIdentifier && ! buildConfig . teamIdentifier ) {
1028+ buildConfig = this . readBuildConfigFromPlatforms ( ) . wait ( ) ;
1029+ }
1030+ }
1031+
1032+ let signingStyle : XcodeSigningStyle ;
1033+ if ( buildConfig . codeSignIdentity || buildConfig . mobileProvisionIdentifier ) {
1034+ signingStyle = "Manual" ;
1035+ } else if ( buildConfig . teamIdentifier ) {
1036+ signingStyle = "Automatic" ;
1037+ } else if ( helpers . isInteractive ( ) ) {
1038+ let signingStyles = [
1039+ "Manual - Select existing provisioning profile for use" ,
1040+ "Automatic - Select Team ID for signing and let Xcode select managed provisioning profile"
1041+ ] ;
1042+ let signingStyleIndex = signingStyles . indexOf ( this . $prompter . promptForChoice ( "Select codesiging style" , signingStyles ) . wait ( ) ) ;
1043+ signingStyle = new Array < XcodeSigningStyle > ( "Manual" , "Automatic" ) [ signingStyleIndex ] ;
1044+
1045+ switch ( signingStyle ) {
1046+ case "Manual" :
1047+ let profile = this . getProvisioningProfile ( ) . wait ( ) ;
1048+ if ( ! profile ) {
1049+ this . $errors . failWithoutHelp ( "No matching provisioning profile found." ) ;
1050+ }
1051+ this . persistProvisioningProfiles ( profile . UUID ) . wait ( ) ;
1052+ this . $logger . info ( "Apply provisioning profile: " + profile . Name + " (" + profile . TeamName + ") " + profile . Type + " UUID: " + profile . UUID ) ;
1053+ buildConfig . mobileProvisionIdentifier = profile . UUID ;
1054+ buildConfig . teamIdentifier = profile . TeamIdentifier [ 0 ] ;
1055+ break ;
1056+ case "Automatic" :
1057+ buildConfig . teamIdentifier = this . getDevelopmentTeam ( ) . wait ( ) ;
1058+ this . persistDevelopmentTeam ( buildConfig . teamIdentifier ) . wait ( ) ;
1059+ break ;
1060+ }
1061+ }
1062+
1063+ if ( signingStyle ) {
1064+ const pbxprojPath = path . join ( projectRoot , this . $projectData . projectName + ".xcodeproj" , "project.pbxproj" ) ;
1065+ const xcode = Xcode . open ( pbxprojPath ) ;
1066+ switch ( signingStyle ) {
1067+ case "Manual" :
1068+ xcode . setManualSigningStyle ( this . $projectData . projectName ) ;
1069+ break ;
1070+ case "Automatic" :
1071+ xcode . setAutomaticSigningStyle ( this . $projectData . projectName , buildConfig . teamIdentifier ) ;
1072+ break ;
1073+ }
1074+ xcode . save ( ) ;
1075+ }
1076+
1077+ return buildConfig ;
1078+ } ) . future < IiOSBuildConfig > ( ) ( ) ;
1079+ }
1080+
10131081 private getDevelopmentTeams ( ) : Array < { id : string , name : string } > {
10141082 let dir = path . join ( process . env . HOME , "Library/MobileDevice/Provisioning Profiles/" ) ;
10151083 let files = this . $fs . readDirectory ( dir ) . wait ( ) ;
@@ -1045,39 +1113,78 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10451113 return null ;
10461114 }
10471115
1048- private readTeamId ( ) : string {
1049- let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1050- if ( this . $fs . exists ( xcconfigFile ) . wait ( ) ) {
1051- let text = this . $fs . readText ( xcconfigFile ) . wait ( ) ;
1052- let teamId : string ;
1053- text . split ( / \r ? \n / ) . forEach ( ( line ) => {
1054- 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 ) ;
1059- }
1060- }
1061- } ) ;
1062- if ( teamId ) {
1063- return teamId ;
1116+ private readXCConfigSigning ( ) : IFuture < IiOSBuildConfig > {
1117+ return ( ( ) => {
1118+ const result : IiOSBuildConfig = { } ;
1119+ let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1120+ if ( this . $fs . exists ( xcconfigFile ) . wait ( ) ) {
1121+ let text = this . $fs . readText ( xcconfigFile ) . wait ( ) ;
1122+ text . split ( / \r ? \n / ) . forEach ( ( line ) => {
1123+ line = line . replace ( / \/ ( \/ ) [ ^ \n ] * $ / , "" ) ;
1124+ const read = ( name : string ) => {
1125+ if ( line . indexOf ( name ) >= 0 ) {
1126+ let value = line . substr ( line . lastIndexOf ( "=" ) + 1 ) . trim ( ) ;
1127+ if ( value . charAt ( value . length - 1 ) === ';' ) {
1128+ value = value . substr ( 0 , value . length - 1 ) . trim ( ) ;
1129+ }
1130+ return value ;
1131+ }
1132+ return undefined ;
1133+ } ;
1134+ result . teamIdentifier = read ( "DEVELOPMENT_TEAM" ) || result . teamIdentifier ;
1135+ result . codeSignIdentity = read ( "CODE_SIGN_IDENTITY" ) || result . codeSignIdentity ;
1136+ result . mobileProvisionIdentifier = read ( "PROVISIONING_PROFILE[sdk=iphoneos*]" ) || result . mobileProvisionIdentifier ;
1137+ } ) ;
10641138 }
1065- }
1066- let fileName = path . join ( this . platformData . projectRoot , "teamid" ) ;
1067- if ( this . $fs . exists ( fileName ) . wait ( ) ) {
1068- return this . $fs . readText ( fileName ) . wait ( ) ;
1069- }
1070- return null ;
1139+ return result ;
1140+ } ) . future < provision . MobileProvision > ( ) ( ) ;
10711141 }
10721142
1073- private getDevelopmentTeam ( ) : string {
1074- let teamId : string ;
1075- if ( this . $options . teamId ) {
1076- teamId = this . $options . teamId ;
1077- } else {
1078- teamId = this . readTeamId ( ) ;
1079- }
1080- if ( ! teamId ) {
1143+ private getProvisioningProfile ( ) : IFuture < provision . MobileProvision > {
1144+ return ( ( ) => {
1145+ let profile : provision . MobileProvision ;
1146+
1147+ const allCertificates = cert . read ( ) ;
1148+ const allProfiles = provision . read ( ) ;
1149+ const query : provision . Query = {
1150+ Certificates : allCertificates . valid ,
1151+ AppId : this . $projectData . projectId ,
1152+ Type : "Development"
1153+ } ;
1154+
1155+ if ( this . $options . device ) {
1156+ query . ProvisionedDevices = [ this . $options . device ] ;
1157+ } else {
1158+ this . $devicesService . initialize ( ) . wait ( ) ;
1159+ let deviceUDIDs = _ ( this . $devicesService . getDeviceInstances ( ) )
1160+ . filter ( d => this . $mobileHelper . isiOSPlatform ( d . deviceInfo . platform ) )
1161+ . map ( d => d . deviceInfo . identifier )
1162+ . toJSON ( ) ;
1163+ query . ProvisionedDevices = deviceUDIDs ;
1164+ }
1165+
1166+ const result = provision . select ( allProfiles , query ) ;
1167+ const choiceMap = result . eligable . reduce ( ( acc , p ) => {
1168+ acc [ `'${ p . Name } ' (${ p . TeamName } ) ${ p . Type } ` ] = p ;
1169+ return acc ;
1170+ } , < { [ display : string ] : provision . MobileProvision } > { } ) ;
1171+
1172+ const choices = Object . keys ( choiceMap ) ;
1173+ if ( choices . length > 0 ) {
1174+ const choice = this . $prompter . promptForChoice (
1175+ `Select provisioning profiles (found ${ result . eligable . length } eligable, and ${ result . nonEligable . length } non-eligable)` ,
1176+ choices
1177+ ) . wait ( ) ;
1178+ profile = choiceMap [ choice ] ;
1179+ }
1180+
1181+ return profile ;
1182+ } ) . future < provision . MobileProvision > ( ) ( ) ;
1183+ }
1184+
1185+ private getDevelopmentTeam ( ) : IFuture < string > {
1186+ return ( ( ) => {
1187+ let teamId : string ;
10811188 let teams = this . getDevelopmentTeams ( ) ;
10821189 this . $logger . warn ( "Xcode 8 requires a team id to be specified when building for device." ) ;
10831190 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." ) ;
@@ -1091,27 +1198,75 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10911198 }
10921199 let choice = this . $prompter . promptForChoice ( 'Found multiple development teams, select one:' , choices ) . wait ( ) ;
10931200 teamId = teams [ choices . indexOf ( choice ) ] . id ;
1201+ }
1202+ return teamId ;
1203+ } ) . future < string > ( ) ( ) ;
1204+ }
10941205
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 ;
1111- }
1206+ private persistProvisioningProfiles ( uuid : string ) : IFuture < void > {
1207+ return ( ( ) => {
1208+ let choicesPersist = [
1209+ "Yes, set the PROVISIONING_PROFILE[sdk=iphoneos*] setting in build.xcconfig file." ,
1210+ "Yes, persist the mobileprovision uuid in platforms folder." ,
1211+ "No, don't persist this setting."
1212+ ] ;
1213+ let choicePersist = this . $prompter . promptForChoice ( "Do you want to make mobileprovision: " + uuid + " a persistent choice for your app?" , choicesPersist ) . wait ( ) ;
1214+ switch ( choicesPersist . indexOf ( choicePersist ) ) {
1215+ case 0 :
1216+ let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1217+ this . $fs . appendFile ( xcconfigFile , "\nPROVISIONING_PROFILE[sdk=iphoneos*] = " + uuid + "\n" ) . wait ( ) ;
1218+ break ;
1219+ case 1 :
1220+ this . $fs . writeFile ( path . join ( this . platformData . projectRoot , "mobileprovision" ) , uuid ) . wait ( ) ;
1221+ const teamidPath = path . join ( this . platformData . projectRoot , "teamid" ) ;
1222+ if ( this . $fs . exists ( teamidPath ) . wait ( ) ) {
1223+ this . $fs . deleteFile ( teamidPath ) . wait ( ) ;
1224+ }
1225+ break ;
1226+ default :
1227+ break ;
11121228 }
1113- }
1114- return teamId ;
1229+ } ) . future < void > ( ) ( ) ;
1230+ }
1231+
1232+ private persistDevelopmentTeam ( teamId : string ) : IFuture < void > {
1233+ return ( ( ) => {
1234+ let choicesPersist = [
1235+ "Yes, set the DEVELOPMENT_TEAM setting in build.xcconfig file." ,
1236+ "Yes, persist the team id in platforms folder." ,
1237+ "No, don't persist this setting."
1238+ ] ;
1239+ let choicePersist = this . $prompter . promptForChoice ( "Do you want to make teamId: " + teamId + " a persistent choice for your app?" , choicesPersist ) . wait ( ) ;
1240+ switch ( choicesPersist . indexOf ( choicePersist ) ) {
1241+ case 0 :
1242+ let xcconfigFile = path . join ( this . $projectData . appResourcesDirectoryPath , this . platformData . normalizedPlatformName , "build.xcconfig" ) ;
1243+ this . $fs . appendFile ( xcconfigFile , "\nDEVELOPMENT_TEAM = " + teamId + "\n" ) . wait ( ) ;
1244+ break ;
1245+ case 1 :
1246+ this . $fs . writeFile ( path . join ( this . platformData . projectRoot , "teamid" ) , teamId ) . wait ( ) ;
1247+ const mobileprovisionPath = path . join ( this . platformData . projectRoot , "mobileprovision" ) ;
1248+ if ( this . $fs . exists ( mobileprovisionPath ) . wait ( ) ) {
1249+ this . $fs . deleteFile ( mobileprovisionPath ) . wait ( ) ;
1250+ }
1251+ break ;
1252+ default :
1253+ break ;
1254+ }
1255+ } ) . future < void > ( ) ( ) ;
1256+ }
1257+
1258+ private readBuildConfigFromPlatforms ( ) : IFuture < IiOSBuildConfig > {
1259+ return ( ( ) => {
1260+ let mobileprovisionPath = path . join ( this . platformData . projectRoot , "mobileprovision" ) ;
1261+ if ( this . $fs . exists ( mobileprovisionPath ) . wait ( ) ) {
1262+ return { mobileProvisionIdentifier : this . $fs . readText ( mobileprovisionPath ) . wait ( ) } ;
1263+ }
1264+ let teamidPath = path . join ( this . platformData . projectRoot , "teamid" ) ;
1265+ if ( this . $fs . exists ( teamidPath ) . wait ( ) ) {
1266+ return { teamIdentifier : this . $fs . readText ( teamidPath ) . wait ( ) } ;
1267+ }
1268+ return < IiOSBuildConfig > { } ;
1269+ } ) . future < IiOSBuildConfig > ( ) ( ) ;
11151270 }
11161271}
11171272
0 commit comments