diff --git a/xcode/commonFastfile b/xcode/commonFastfile index 6e8b230..0258809 100644 --- a/xcode/commonFastfile +++ b/xcode/commonFastfile @@ -13,19 +13,37 @@ private_lane :installDependencies do |options| bundle_install(path: "../.gem") end - if File.exists? "../Cartfile" - begin - carthage(command: "bootstrap", platform: "iOS") - rescue - # workaround for https://github.com/Carthage/Carthage/issues/2298 - sh("rm -rf ~/Library/Caches/org.carthage.CarthageKit") - carthage(command: "update", platform: "iOS") - end - end - cocoapods( repo_update: true ) + + if File.exists? "../Cartfile" + use_rome = File.exists? "../Romefile" + + swift_version = sh("xcrun swift --version | head -1 | sed 's/.*\\(\(.*\)\\).*/\\1/' | tr -d \"()\" | tr \" \" \"-\"").chop + rome_path = "Pods/Rome/rome" + rome_options = "--platform iOS --cache-prefix #{swift_version} --romefile Romefile" + + carthage_install = lambda do + if use_rome + sh("cd .. && #{rome_path} download #{rome_options}") + end + + carthage(command: "bootstrap", platform: "iOS", cache_builds: true) + + if use_rome + sh("cd .. && #{rome_path} list --missing #{rome_options} | awk '{print $1}' | xargs -I framework_name #{rome_path} upload framework_name #{rome_options}") + end + end + + begin + carthage_install.call + rescue + # workaround for https://github.com/Carthage/Carthage/issues/2298 + sh("rm -rf ~/Library/Caches/org.carthage.CarthageKit") + carthage_install.call + end + end end private_lane :uploadToFabric do |options| @@ -126,6 +144,12 @@ private_lane :buildArchive do |options| exportOptions = icloudEnvironment.to_s.empty? ? {} : {iCloudContainerEnvironment: icloudEnvironment} exportOptions[:compileBitcode] = options[:compileBitcode] || false + xcconfig_name = options[:configuration] + configuration_name = options[:configuration_name] + xcodeproj_path = options[:xcodeproj_path] + + set_xcconfig_for_configuration_of_project(xcconfig_name, configuration_name, xcodeproj_path) + gym( clean: true, workspace: options[:workspace], @@ -133,7 +157,7 @@ private_lane :buildArchive do |options| archive_path: "./", output_directory: "./", output_name: options[:output_name], - configuration: options[:configuration], + configuration: configuration_name, export_method: options[:method], export_options: exportOptions, skip_package_ipa: options[:skip_package_ipa], @@ -212,7 +236,7 @@ lane :ManuallyUpdateCodeSigning do |options| shallow_clone = false branch = 'fastlane_certificates' - storage_conf = lambda do + storage_conf = lambda do new_storage = Match::Storage.for_mode('git', { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false}) new_storage.download return new_storage @@ -255,7 +279,7 @@ lane :ManuallyUpdateCodeSigning do |options| storage = storage_conf.call encryption = encryption_conf_for_storage.call(storage) - files_to_delete = files_diff.map do |file| + files_to_delete = files_diff.map do |file| old_file = file old_file.slice! old_directory new_file = File.join(storage.working_directory, old_file) @@ -305,7 +329,8 @@ end def make_options_for_lane_name(lane_name) method = configuration_type_from_lane_name(lane_name) return { - :configuration => lane_name, + :configuration => lane_name.start_with?("AppStore") ? "AppStore" : lane_name, + :configuration_name => lane_name.start_with?("AppStore") ? "AppStore" : "Release", :method => method, :type => profile_type_from_configuration_type(method) } @@ -353,3 +378,29 @@ end def fabric_keys_from_shell_script(shell_script_contents) shell_script_contents.gsub("\\n", "").partition('Fabric/run\" ').last.partition('";').first.split(" ") end + +def set_xcconfig_for_configuration_of_project(xcconfig_name, configuration_name, xcodeproj_path) + require 'xcodeproj' + + project = Xcodeproj::Project.open(xcodeproj_path) + + target_to_modify_selector = lambda do |t| + supported_product_types = [ + Xcodeproj::Constants::PRODUCT_TYPE_UTI[:application], + Xcodeproj::Constants::PRODUCT_TYPE_UTI[:app_extension] + ] + return !t.test_target_type? && supported_product_types.include?(t.product_type) +end + + application_targets = project.native_targets.select(&target_to_modify_selector) + + application_targets.each do |target| + build_configuration = target.build_configuration_list[configuration_name] + config_name = target.name + xcconfig_name + build_configuration_reference = project.files.select { |f| f.path.start_with?(config_name) }.first + build_configuration.base_configuration_reference = build_configuration_reference + end + + + project.save() +end diff --git a/xcode/config_generator/custom_settings.json b/xcode/config_generator/custom_settings.json new file mode 100644 index 0000000..a190324 --- /dev/null +++ b/xcode/config_generator/custom_settings.json @@ -0,0 +1,25 @@ +{ + "targets": [ + { + "YOUR_TARGET_NAME": { + "Standard": { + "PROVISIONING_PROFILE_SPECIFIER": "", + "PRODUCT_BUNDLE_IDENTIFIER": "", + "CODE_SIGN_ENTITLEMENTS": "" + }, + + "Enterprise": { + "PROVISIONING_PROFILE_SPECIFIER": "", + "PRODUCT_BUNDLE_IDENTIFIER": "", + "CODE_SIGN_ENTITLEMENTS": "" + }, + + "AppStore": { + "DEVELOPMENT_TEAM": "", + "PRODUCT_BUNDLE_IDENTIFIER": "", + "CODE_SIGN_ENTITLEMENTS": "" + } + } + } + ] +} diff --git a/xcode/config_generator/gen_configurations.py b/xcode/config_generator/gen_configurations.py new file mode 100644 index 0000000..22845ed --- /dev/null +++ b/xcode/config_generator/gen_configurations.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import unicode_literals # python 2/3 support + +from itertools import chain +import json + +distribution_options = ["Enterprise", "Standard"] +server_type_options = ["Mock", "Touchin", "Customer"] +server_environment_options = ["Dev", "Test", "Stage", "Prod"] +ssl_pinning_options = ["WithSSLPinning", "WithoutSSLPinning"] +build_type_options = ["Debug", "Release"] + +all_options = [ + distribution_options, + server_type_options, + server_environment_options, + ssl_pinning_options, + build_type_options +] + +def combine_string_with_options(all_options, string="", applied_options=[]): + if len(all_options) == 0: + yield string, applied_options + return + + for current_option in chain.from_iterable(all_options[:1]): + for result_tuple in combine_string_with_options(all_options[1:], string + current_option, applied_options + [current_option]): + yield result_tuple + + yield ("AppStoreRelease", ['AppStore', 'Customer', 'Prod', 'WithSSLPinning', 'Release']) + +def make_config_dict(args): + config_name, applied_options = args + + if "Enterprise" in applied_options: + account_type = "Enterprise" + elif "Standard" in applied_options: + account_type = "Standard" + else: + account_type = "AppStore" + + if "Debug" in applied_options: + build_type = "debug" + elif "AppStore" in applied_options: + build_type = "appstore" + else: + build_type = "release" + + return { + "name": config_name, + "build_type": build_type, + "account_type": account_type, + "xcconfig_options": [ + { + "key": "SWIFT_ACTIVE_COMPILATION_CONDITIONS", + "value": " ".join(map(lambda option: option.upper(), applied_options)) + }, + { + "key": "DEBUG_INFORMATION_FORMAT", + "value": "dwarf" if "Debug" in applied_options else "dwarf-with-dsym" + }, + { + "key": "VALIDATE_PRODUCT", + "value": "NO" if "Debug" in applied_options else "YES" + }, + { + "key": "ENABLE_TESTABILITY", + "value": "YES" if "Debug" in applied_options else "NO" + }, + { + "key": "CODE_SIGN_IDENTITY", + "value": "iPhone Developer" if account_type == "Standard" else "iPhone Distribution" + }, + { + "key": "GCC_OPTIMIZATION_LEVEL", + "value": "0" if "Debug" in applied_options else "s" + }, + { + "key": "SWIFT_OPTIMIZATION_LEVEL", + "value": "-Onone" if "Debug" in applied_options else "-O" + }, + { + "key": "SWIFT_COMPILATION_MODE", + "value": "singlefile" if "Debug" in applied_options else "wholemodule" + } + ] + } + + +config_dicts = map(make_config_dict, combine_string_with_options(all_options)) + +print(json.dumps({"configurations": config_dicts}, indent=4)) diff --git a/xcode/config_generator/render_xcconfigs.rb b/xcode/config_generator/render_xcconfigs.rb new file mode 100644 index 0000000..dbe8d84 --- /dev/null +++ b/xcode/config_generator/render_xcconfigs.rb @@ -0,0 +1,116 @@ +require 'json' +require 'mustache' + +# Constants +$configs_folder_name = "TargetConfigurations" +$standard_dev_team = "D4HA43V467" +$enterprise_dev_team = "228J5MMU7S" +$standard_bundle_prefix = "ru.touchin." +$enterprise_bundle_prefix = "com.touchin." +$bundle_id_key = "PRODUCT_BUNDLE_IDENTIFIER" + +# create config directory if needed +Dir.mkdir($configs_folder_name) unless Dir.exist?($configs_folder_name) + +# call python script and generate configs to config file +system("python gen_configurations.py > configs_data.json") + + +# open settings + template file +settings = JSON.load(File.open("custom_settings.json")) +target_xcconfig_tempate = File.read("target_xcconfig.mustache") + + +# set global property +targets = settings["targets"] + +# make tuple of key and value become mustache template element +def config_option(key, value) + return { "key" => key, "value" => value } +end + +# return empty array or generated dev team hash +def generate_development_team(development_team_key, account_type) + team_value = account_type == "Standard" ? $standard_dev_team : $enterprise_dev_team + return config_option(development_team_key, team_value) +end + +# return empty array or generated provisioning profile hash +def generate_provisioning_profile(provisioning_key, bundle_id, account_type) + if account_type == "AppStore" + app_store_profiile = "match AppStore " + bundle_id + return config_option(provisioning_key, app_store_profiile) + else + return config_option(provisioning_key, bundle_id) + end +end + +def generate_bundle_id(target_name, account_type) + bundle_id_prefix = account_type == "Standard" ? $standard_bundle_prefix : $enterprise_bundle_prefix + bundle_id = bundle_id_prefix + target_name + return config_option($bundle_id_key, bundle_id) +end + +# generate missing properties if needed +def generate_missing_properties(target_name, properties, account_type) + result = [] + development_team_key = "DEVELOPMENT_TEAM" + provisioning_key = "PROVISIONING_PROFILE_SPECIFIER" + + unless properties.key?($bundle_id_key) + bundle_id_config = generate_bundle_id(target_name, account_type) + bundle_id = bundle_id_config["value"] + result.append(bundle_id_config) + else + bundle_id = properties[$bundle_id_key] + end + + unless properties.key?(development_team_key) + result.append(generate_development_team(development_team_key, account_type)) + end + + unless properties.key?(provisioning_key) + result.append(generate_provisioning_profile(provisioning_key, bundle_id, account_type)) + end + + return result +end + +# run through all target in project +targets.each do |target| + + # need open everytime, because script make some changes only for this target + configs = JSON.load(File.open("configs_data.json"))["configurations"] + + # run through all configs + configs.each do |config| + + # take default values + account_type = config["account_type"] + target_name = target.keys.first + properties = target[target_name][account_type] + + # add properties from settings file + properties.each do |key, value| + config["xcconfig_options"].append(config_option(key, value)) + end + + # add missing properties if needed + config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, account_type)) + + # create settings pack + config_data = { + "target_name": target_name, + "configuration": config + } + + # create file for every setting in loop + File.open($configs_folder_name + "/" + target_name + config["name"] + ".xcconfig", 'w') { |file| + file.puts(Mustache.render(target_xcconfig_tempate, config_data)) + } + end + +end + +# remove config file, it's trash +File.delete("configs_data.json") if File.exist?("configs_data.json") diff --git a/xcode/config_generator/target_xcconfig.mustache b/xcode/config_generator/target_xcconfig.mustache new file mode 100644 index 0000000..7e5477a --- /dev/null +++ b/xcode/config_generator/target_xcconfig.mustache @@ -0,0 +1,7 @@ +#include "Pods/Target Support Files/Pods-{{target_name}}/Pods-{{target_name}}.{{configuration.build_type}}.xcconfig" + +{{#configuration.xcconfig_options}} +{{key}} = {{value}} +{{/configuration.xcconfig_options}} + +CODE_SIGN_STYLE = Manual \ No newline at end of file