1c4ef02aeSEvan Baconimport { IOSConfig, XcodeProject } from '@expo/config-plugins'; 2c4ef02aeSEvan Baconimport fs from 'fs'; 3c4ef02aeSEvan Bacon 4c4ef02aeSEvan Baconexport type CodeSigningInfo = Record< 5c4ef02aeSEvan Bacon string, 6c4ef02aeSEvan Bacon { 7c4ef02aeSEvan Bacon developmentTeams: string[]; 8c4ef02aeSEvan Bacon provisioningProfiles: string[]; 9c4ef02aeSEvan Bacon } 10c4ef02aeSEvan Bacon>; 11c4ef02aeSEvan Bacon 12c4ef02aeSEvan Bacon/** Find the development team and provisioning profile that's currently in use by the Xcode project. */ 13c4ef02aeSEvan Baconexport function getCodeSigningInfoForPbxproj(projectRoot: string): CodeSigningInfo { 14c4ef02aeSEvan Bacon const project = IOSConfig.XcodeUtils.getPbxproj(projectRoot); 15c4ef02aeSEvan Bacon const targets = IOSConfig.Target.findSignableTargets(project); 16c4ef02aeSEvan Bacon 17c4ef02aeSEvan Bacon const signingInfo: CodeSigningInfo = {}; 18c4ef02aeSEvan Bacon for (const [nativeTargetId, nativeTarget] of targets) { 19c4ef02aeSEvan Bacon const developmentTeams: string[] = []; 20c4ef02aeSEvan Bacon const provisioningProfiles: string[] = []; 21c4ef02aeSEvan Bacon 22c4ef02aeSEvan Bacon IOSConfig.XcodeUtils.getBuildConfigurationsForListId( 23c4ef02aeSEvan Bacon project, 24c4ef02aeSEvan Bacon nativeTarget.buildConfigurationList 25c4ef02aeSEvan Bacon ) 26c4ef02aeSEvan Bacon .filter( 27c4ef02aeSEvan Bacon ([, item]: IOSConfig.XcodeUtils.ConfigurationSectionEntry) => 28c4ef02aeSEvan Bacon item.buildSettings.PRODUCT_NAME 29c4ef02aeSEvan Bacon ) 30c4ef02aeSEvan Bacon .forEach(([, item]: IOSConfig.XcodeUtils.ConfigurationSectionEntry) => { 31c4ef02aeSEvan Bacon const { DEVELOPMENT_TEAM, PROVISIONING_PROFILE } = item.buildSettings; 32c4ef02aeSEvan Bacon if ( 33c4ef02aeSEvan Bacon typeof DEVELOPMENT_TEAM === 'string' && 34c4ef02aeSEvan Bacon // If the user selects "Team: none" in Xcode, it'll be an empty string. 35c4ef02aeSEvan Bacon !!DEVELOPMENT_TEAM && 36c4ef02aeSEvan Bacon // xcode package sometimes reads an empty string as a quoted empty string. 37c4ef02aeSEvan Bacon DEVELOPMENT_TEAM !== '""' 38c4ef02aeSEvan Bacon ) { 39c4ef02aeSEvan Bacon developmentTeams.push(DEVELOPMENT_TEAM); 40c4ef02aeSEvan Bacon } 41c4ef02aeSEvan Bacon if (typeof PROVISIONING_PROFILE === 'string' && !!PROVISIONING_PROFILE) { 42c4ef02aeSEvan Bacon provisioningProfiles.push(PROVISIONING_PROFILE); 43c4ef02aeSEvan Bacon } 44c4ef02aeSEvan Bacon }); 45c4ef02aeSEvan Bacon signingInfo[nativeTargetId] = { 46c4ef02aeSEvan Bacon developmentTeams, 47c4ef02aeSEvan Bacon provisioningProfiles, 48c4ef02aeSEvan Bacon }; 49c4ef02aeSEvan Bacon } 50c4ef02aeSEvan Bacon 51c4ef02aeSEvan Bacon return signingInfo; 52c4ef02aeSEvan Bacon} 53c4ef02aeSEvan Bacon 54c4ef02aeSEvan Bacon/** 55c4ef02aeSEvan Bacon * Set the development team and configure the Xcode project for automatic code signing, 56c4ef02aeSEvan Bacon * this helps us resolve the code signing on subsequent runs and emulates Xcode behavior. 57c4ef02aeSEvan Bacon * 58c4ef02aeSEvan Bacon * @param props.project xcode project object from `xcode` package. 59c4ef02aeSEvan Bacon * @param props.appleTeamId Apple Team ID to use for code signing. 60c4ef02aeSEvan Bacon */ 61c4ef02aeSEvan Baconexport function mutateXcodeProjectWithAutoCodeSigningInfo({ 62c4ef02aeSEvan Bacon project, 63c4ef02aeSEvan Bacon appleTeamId, 64c4ef02aeSEvan Bacon}: { 65c4ef02aeSEvan Bacon project: XcodeProject; 66c4ef02aeSEvan Bacon appleTeamId: string; 67c4ef02aeSEvan Bacon}): XcodeProject { 68c4ef02aeSEvan Bacon const targets = IOSConfig.Target.findSignableTargets(project); 69c4ef02aeSEvan Bacon 70c4ef02aeSEvan Bacon const quotedAppleTeamId = ensureQuotes(appleTeamId); 71c4ef02aeSEvan Bacon 72c4ef02aeSEvan Bacon for (const [nativeTargetId, nativeTarget] of targets) { 73c4ef02aeSEvan Bacon IOSConfig.XcodeUtils.getBuildConfigurationsForListId( 74c4ef02aeSEvan Bacon project, 75c4ef02aeSEvan Bacon nativeTarget.buildConfigurationList 76c4ef02aeSEvan Bacon ) 77c4ef02aeSEvan Bacon .filter( 78c4ef02aeSEvan Bacon ([, item]: IOSConfig.XcodeUtils.ConfigurationSectionEntry) => 79c4ef02aeSEvan Bacon item.buildSettings.PRODUCT_NAME 80c4ef02aeSEvan Bacon ) 81c4ef02aeSEvan Bacon .forEach(([, item]: IOSConfig.XcodeUtils.ConfigurationSectionEntry) => { 82c4ef02aeSEvan Bacon item.buildSettings.DEVELOPMENT_TEAM = quotedAppleTeamId; 83c4ef02aeSEvan Bacon item.buildSettings.CODE_SIGN_IDENTITY = '"Apple Development"'; 84c4ef02aeSEvan Bacon item.buildSettings.CODE_SIGN_STYLE = 'Automatic'; 85c4ef02aeSEvan Bacon }); 86c4ef02aeSEvan Bacon 87c4ef02aeSEvan Bacon Object.entries(IOSConfig.XcodeUtils.getProjectSection(project)) 88c4ef02aeSEvan Bacon .filter(IOSConfig.XcodeUtils.isNotComment) 89c4ef02aeSEvan Bacon .forEach(([, item]: IOSConfig.XcodeUtils.ProjectSectionEntry) => { 90*177f3567SEvan Bacon if (!item.attributes.TargetAttributes) { 91*177f3567SEvan Bacon item.attributes.TargetAttributes = {}; 92*177f3567SEvan Bacon } 93*177f3567SEvan Bacon 94c4ef02aeSEvan Bacon if (!item.attributes.TargetAttributes[nativeTargetId]) { 95c4ef02aeSEvan Bacon item.attributes.TargetAttributes[nativeTargetId] = {}; 96c4ef02aeSEvan Bacon } 97c4ef02aeSEvan Bacon 98c4ef02aeSEvan Bacon item.attributes.TargetAttributes[nativeTargetId].DevelopmentTeam = quotedAppleTeamId; 99c4ef02aeSEvan Bacon item.attributes.TargetAttributes[nativeTargetId].ProvisioningStyle = 'Automatic'; 100c4ef02aeSEvan Bacon }); 101c4ef02aeSEvan Bacon } 102c4ef02aeSEvan Bacon 103c4ef02aeSEvan Bacon return project; 104c4ef02aeSEvan Bacon} 105c4ef02aeSEvan Bacon 106c4ef02aeSEvan Bacon/** 107c4ef02aeSEvan Bacon * Configures the Xcode project for automatic code signing and persists the results. 108c4ef02aeSEvan Bacon */ 109c4ef02aeSEvan Baconexport function setAutoCodeSigningInfoForPbxproj( 110c4ef02aeSEvan Bacon projectRoot: string, 111c4ef02aeSEvan Bacon { appleTeamId }: { appleTeamId: string } 112c4ef02aeSEvan Bacon): void { 113c4ef02aeSEvan Bacon const project = IOSConfig.XcodeUtils.getPbxproj(projectRoot); 114c4ef02aeSEvan Bacon mutateXcodeProjectWithAutoCodeSigningInfo({ project, appleTeamId }); 115c4ef02aeSEvan Bacon 116c4ef02aeSEvan Bacon fs.writeFileSync(project.filepath, project.writeSync()); 117c4ef02aeSEvan Bacon} 118c4ef02aeSEvan Bacon 119c4ef02aeSEvan Baconconst ensureQuotes = (value: string) => { 120c4ef02aeSEvan Bacon if (!value.match(/^['"]/)) { 121c4ef02aeSEvan Bacon return `"${value}"`; 122c4ef02aeSEvan Bacon } 123c4ef02aeSEvan Bacon return value; 124c4ef02aeSEvan Bacon}; 125