@@ -11,6 +11,10 @@ import { PlistSession } from "plist-merge-patch";
11
11
import { EOL } from "os" ;
12
12
import * as temp from "temp" ;
13
13
import * 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" ;
14
18
15
19
export class IOSProjectService extends projectServiceBaseLib . PlatformProjectServiceBase implements IPlatformProjectService {
16
20
private static XCODE_PROJECT_EXT_NAME = ".xcodeproj" ;
@@ -300,10 +304,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
300
304
301
305
let xcodeBuildVersion = this . getXcodeVersion ( ) ;
302
306
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 ( ) ;
307
308
}
308
309
309
310
if ( buildConfig && buildConfig . codeSignIdentity ) {
@@ -314,6 +315,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
314
315
args . push ( `PROVISIONING_PROFILE=${ buildConfig . mobileProvisionIdentifier } ` ) ;
315
316
}
316
317
318
+ if ( buildConfig && buildConfig . teamIdentifier ) {
319
+ args . push ( `DEVELOPMENT_TEAM=${ buildConfig . teamIdentifier } ` ) ;
320
+ }
321
+
317
322
this . $childProcess . spawnFromEvent ( "xcodebuild" , args , "exit" , { cwd : this . $options , stdio : 'inherit' } ) . wait ( ) ;
318
323
this . createIpa ( projectRoot ) . wait ( ) ;
319
324
@@ -1010,6 +1015,69 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
1010
1015
return xcodeBuildVersion ;
1011
1016
}
1012
1017
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
+
1013
1081
private getDevelopmentTeams ( ) : Array < { id : string , name : string } > {
1014
1082
let dir = path . join ( process . env . HOME , "Library/MobileDevice/Provisioning Profiles/" ) ;
1015
1083
let files = this . $fs . readDirectory ( dir ) . wait ( ) ;
@@ -1045,39 +1113,78 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
1045
1113
return null ;
1046
1114
}
1047
1115
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
+ } ) ;
1064
1138
}
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 > ( ) ( ) ;
1071
1141
}
1072
1142
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 ;
1081
1188
let teams = this . getDevelopmentTeams ( ) ;
1082
1189
this . $logger . warn ( "Xcode 8 requires a team id to be specified when building for device." ) ;
1083
1190
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
1091
1198
}
1092
1199
let choice = this . $prompter . promptForChoice ( 'Found multiple development teams, select one:' , choices ) . wait ( ) ;
1093
1200
teamId = teams [ choices . indexOf ( choice ) ] . id ;
1201
+ }
1202
+ return teamId ;
1203
+ } ) . future < string > ( ) ( ) ;
1204
+ }
1094
1205
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 ;
1112
1228
}
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 > ( ) ( ) ;
1115
1270
}
1116
1271
}
1117
1272
0 commit comments