From d57333f5d1b933f88ba4e6ced9a0fa67d0c2cfc4 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Tue, 28 Apr 2020 18:07:13 +0300 Subject: [PATCH 1/3] add SyncSymbols lane; make code a little better --- xcode/commonFastfile | 148 ++++++++---------- xcode/fastlane/touchlane/lib/configuration.rb | 48 ++++++ .../touchlane/lib/configuration_type.rb | 63 ++++++++ xcode/fastlane/touchlane/lib/touchlane.rb | 4 + 4 files changed, 183 insertions(+), 80 deletions(-) create mode 100644 xcode/fastlane/touchlane/lib/configuration.rb create mode 100644 xcode/fastlane/touchlane/lib/configuration_type.rb create mode 100644 xcode/fastlane/touchlane/lib/touchlane.rb diff --git a/xcode/commonFastfile b/xcode/commonFastfile index b24227f..de149df 100644 --- a/xcode/commonFastfile +++ b/xcode/commonFastfile @@ -1,5 +1,7 @@ $appName = File.basename(Dir['../*.xcworkspace'].first, '.*') +require_relative 'fastlane/touchlane/lib/touchlane' + private_lane :installDependencies do |options| podsReposPath = File.expand_path "~/.cocoapods/repos/master/" lockFilePath = "#{podsReposPath}/.git/index.lock" @@ -52,13 +54,24 @@ private_lane :uploadToFirebase do |options| sh("yarn install") + app_target_folder_name = options[:appName] || $appName + configuration_type = Touchlane::ConfigurationType.from_type(options[:type]) + + gsp_plist_path = get_google_services_plist_path(app_target_folder_name, configuration_type) + + google_app_id = get_info_plist_value(path: gsp_plist_path, key: "GOOGLE_APP_ID") + firebase_app_distribution( - app: get_google_app_identifier(options), + app: google_app_id, ipa_path: options[:ipa_path], groups: "touch-instinct", release_notes_file: releaseNotesFile, firebase_cli_path: File.expand_path("../node_modules/firebase-tools/lib/bin/firebase.js") ) + + upload_symbols_to_crashlytics( + gsp_path: get_google_services_plist_path(app_target_folder_name, configuration_type) + ) end private_lane :uploadToAppStore do |options| @@ -75,13 +88,18 @@ end private_lane :buildConfiguration do |options| appName = options[:appName] || $appName + lane_name = lane_context[SharedValues::LANE_NAME] + options[:scheme] = appName + options[:xcconfig_name] = lane_name - configuration = options[:configuration] || lane_context[SharedValues::LANE_NAME] - options = options.merge(make_options_for_lane_name(configuration)) - options = merge_options_with_config_file(options) + configuration_type = Touchlane::ConfigurationType.from_lane_name(lane_name) + configuration = get_configuration_for_type(configuration_type.type) - options = options.merge(get_keychain_options(options)) + options = configuration.to_options() + .merge(configuration_type.to_options()) + .merge(get_keychain_options(options)) + .merge(options) openKeychain(options) @@ -103,7 +121,6 @@ private_lane :buildConfiguration do |options| installDependencies(options) if !(options[:uploadToFabric] || options[:uploadToAppStore]) - options = merge_options_with_config_file(options) options[:skip_package_ipa] = true syncCodeSigning(options) @@ -112,8 +129,6 @@ private_lane :buildConfiguration do |options| end if options[:uploadToFabric] - options = merge_options_with_config_file(options) - syncCodeSigning(options) buildArchive(options) @@ -123,8 +138,6 @@ private_lane :buildConfiguration do |options| if options[:uploadToAppStore] options[:compileBitcode] = options[:compileBitcode].nil? ? true : options[:compileBitcode] options[:include_symbols] = options[:include_symbols].nil? ? true : options[:include_symbols] - options = options.merge(make_options_for_lane_name("AppStore")) - options = merge_options_with_config_file(options) syncCodeSigning(options) @@ -138,11 +151,11 @@ 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] + xcconfig_name = options[:xcconfig_name] + configuration = options[:configuration] xcodeproj_path = options[:xcodeproj_path] - set_xcconfig_for_configuration_of_project(xcconfig_name, configuration_name, xcodeproj_path) + set_xcconfig_for_configuration_of_project(xcconfig_name, configuration, xcodeproj_path) gym( clean: true, @@ -151,8 +164,8 @@ private_lane :buildArchive do |options| archive_path: "./", output_directory: "./", output_name: options[:output_name], - configuration: configuration_name, - export_method: options[:method], + configuration: configuration, + export_method: options[:export_method], export_options: exportOptions, skip_package_ipa: options[:skip_package_ipa], include_symbols: options[:include_symbols] || false, @@ -189,6 +202,8 @@ lane :syncCodeSigning do |options| storage_mode: "git", git_url: options[:git_url], git_branch: "fastlane_certificates", + shallow_clone: true, + clone_branch_directly: true, keychain_name: options[:keychain_name], keychain_password: options[:keychain_password], skip_docs: true, @@ -196,6 +211,36 @@ lane :syncCodeSigning do |options| ) end +lane :SyncSymbols do |options| + configuration = get_configuration_for_type(options[:type]) + options = configuration.to_options().merge(options) + + appName = options[:appName] || $appName + + xcodeproj_path = File.expand_path "../#{appName}.xcodeproj" + + version_number = options[:version] || get_version_number(xcodeproj: xcodeproj_path, target: appName) + build_number = options[:build_number] || get_build_number(xcodeproj: xcodeproj_path) + + if configuration.type.is_app_store + download_dsyms( + username: options[:username], + app_identifier: options[:app_identifier].first, + team_id: options[:itc_team_id], + version: version_number, + build_number: build_number + ) + end + + app_target_folder_name = appName + + upload_symbols_to_crashlytics( + gsp_path: get_google_services_plist_path(app_target_folder_name, configuration.type) + ) + + clean_build_artifacts +end + private_lane :openKeychain do |options| if is_ci? # workaround to avoid duplication problem @@ -305,74 +350,17 @@ def get_keychain_options(options) return {:keychain_name => keychain_name, :keychain_password => keychain_password} end -def configuration_type_from_lane_name(lane_name) - case - when lane_name.start_with?("Enterprise") - "enterprise" - when lane_name.start_with?("AppStore") - "app-store" - else - "development" - end +def get_configuration_for_type(type) + config_path = File.expand_path "configurations.yaml" + + configuration = Touchlane::Configuration.from_file(config_path, type) end -def profile_type_from_configuration_type(configuration_type) - configuration_type.gsub("-", "") # "app-store" => "appstore" +def get_google_services_plist_path(app_target_folder_name, configuration_type) + File.expand_path "../#{app_target_folder_name}/Resources/#{configuration_type.prefix}-GoogleService-Info.plist" end -def make_options_for_lane_name(lane_name) - method = configuration_type_from_lane_name(lane_name) - return { - :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) - } -end - -def merge_options_with_config_file(options) - type = options[:type] || "development" - - options_override = load_options_from("configurations.yaml") - return options.merge(options_override[type.to_sym]) -end - -def load_options_from(file_path) - if File.exists? file_path - require "yaml" - - options = YAML.load_file(file_path) - - return symbolize_keys(options) - end - - return nil -end - -# http://www.virtuouscode.com/2009/07/14/recursively-symbolize-keys/ -def symbolize_keys(hash) - hash.inject({}){|result, (key, value)| - new_key = case key - when String then key.to_sym - else key - end - new_value = case value - when Hash then symbolize_keys(value) - else value - end - result[new_key] = new_value - result - } -end - -def get_google_app_identifier(options) - file_name_prefix = options[:type].capitalize - appName = options[:appName] || $appName - file_path = File.expand_path "../#{appName}/Resources/#{file_name_prefix}-GoogleService-Info.plist" - get_info_plist_value(path: file_path, key: "GOOGLE_APP_ID") -end - -def set_xcconfig_for_configuration_of_project(xcconfig_name, configuration_name, xcodeproj_path) +def set_xcconfig_for_configuration_of_project(xcconfig_name, configuration, xcodeproj_path) require 'xcodeproj' project = Xcodeproj::Project.open(xcodeproj_path) @@ -388,7 +376,7 @@ end application_targets = project.native_targets.select(&target_to_modify_selector) application_targets.each do |target| - build_configuration = target.build_configuration_list[configuration_name] + build_configuration = target.build_configuration_list[configuration] 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 diff --git a/xcode/fastlane/touchlane/lib/configuration.rb b/xcode/fastlane/touchlane/lib/configuration.rb new file mode 100644 index 0000000..a12c764 --- /dev/null +++ b/xcode/fastlane/touchlane/lib/configuration.rb @@ -0,0 +1,48 @@ +require "yaml" + +module Touchlane + class Configuration + def initialize(type, app_identifier, apple_id, team_id, itc_team_id) + @type = type + @app_identifier = app_identifier + @apple_id = apple_id + @team_id = team_id + @itc_team_id = itc_team_id + end + + attr_reader :type, :app_identifier, :apple_id, :team_id, :itc_team_id + + def self.from_file(path, type) + hash_of_types = load_hash_of_types_from_file(path) + attrs_hash = hash_of_types[type] + + unless attrs_hash + raise "There is no configuration with type #{type}. Available types: #{attrs_hash.keys}" + else + config_type = Touchlane::ConfigurationType.from_type(type) + new(config_type, attrs_hash["app_identifier"], attrs_hash["apple_id"], attrs_hash["team_id"], attrs_hash["itc_team_id"]) + end + end + + def self.load_hash_of_types_from_file(path) + unless File.exists? path + raise "Unable to load configurations from file at #{path}" + else + YAML.load_file(path) + end + end + + private_class_method :new, :load_hash_of_types_from_file + + def to_options + { + :app_identifier => @app_identifier, + :apple_id => @apple_id, + :username => @apple_id, + :team_id => @team_id, + :itc_team_id => @itc_team_id, + } + .merge(type.to_options) + end + end +end diff --git a/xcode/fastlane/touchlane/lib/configuration_type.rb b/xcode/fastlane/touchlane/lib/configuration_type.rb new file mode 100644 index 0000000..a47434e --- /dev/null +++ b/xcode/fastlane/touchlane/lib/configuration_type.rb @@ -0,0 +1,63 @@ +module Touchlane + class ConfigurationType + DEVELOPMENT = "development" + ENTERPRISE = "enterprise" + APP_STORE = "appstore" + + DEVELOPMENT_PREFIX = "Standard" + ENTERPRISE_PREFIX = "Enterprise" + APP_STORE_PREFIX = "AppStore" + + private_constant :DEVELOPMENT, :ENTERPRISE, :APP_STORE + private_constant :DEVELOPMENT_PREFIX, :ENTERPRISE_PREFIX, :APP_STORE_PREFIX + + def initialize(type) + @type = type + + case type + when DEVELOPMENT, ENTERPRISE + @export_method = type + @configuration = type == DEVELOPMENT ? "Debug" : "Release" + @is_app_store = false + @prefix = type == DEVELOPMENT ? DEVELOPMENT_PREFIX : ENTERPRISE_PREFIX + when APP_STORE + @export_method = "app-store" + @configuration = "AppStore" + @is_app_store = true + @prefix = APP_STORE_PREFIX + else + raise "Unknown type passed #{type}" + end + end + + private_class_method :new + + attr_reader :export_method, :type, :configuration, :is_app_store, :prefix + + def self.from_lane_name(lane_name) + case + when lane_name.start_with?(ENTERPRISE_PREFIX) + from_type(ENTERPRISE) + when lane_name.start_with?(APP_STORE_PREFIX) + from_type(APP_STORE) + when lane_name.start_with?(DEVELOPMENT_PREFIX) + from_type(DEVELOPMENT) + else + raise "Unable to map #{lane_name} to #{ConfigurationType.class}." + + "Available prefixes: #{DEVELOPMENT_PREFIX}, #{ENTERPRISE_PREFIX}, #{APP_STORE_PREFIX}" + end + end + + def self.from_type(type) + new(type) + end + + def to_options + { + :type => @type, + :export_method => @export_method, + :configuration => @configuration + } + end + end +end \ No newline at end of file diff --git a/xcode/fastlane/touchlane/lib/touchlane.rb b/xcode/fastlane/touchlane/lib/touchlane.rb new file mode 100644 index 0000000..8038507 --- /dev/null +++ b/xcode/fastlane/touchlane/lib/touchlane.rb @@ -0,0 +1,4 @@ +module Touchlane + require_relative "configuration_type" + require_relative "configuration" +end \ No newline at end of file From 8ecb0c68924d15539e9d8fc4cf35c28ced31c685 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 29 Apr 2020 10:39:11 +0300 Subject: [PATCH 2/3] remove useless code --- xcode/commonFastfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/xcode/commonFastfile b/xcode/commonFastfile index de149df..56d268e 100644 --- a/xcode/commonFastfile +++ b/xcode/commonFastfile @@ -96,8 +96,7 @@ private_lane :buildConfiguration do |options| configuration_type = Touchlane::ConfigurationType.from_lane_name(lane_name) configuration = get_configuration_for_type(configuration_type.type) - options = configuration.to_options() - .merge(configuration_type.to_options()) + options = configuration.to_options .merge(get_keychain_options(options)) .merge(options) @@ -213,7 +212,7 @@ end lane :SyncSymbols do |options| configuration = get_configuration_for_type(options[:type]) - options = configuration.to_options().merge(options) + options = configuration.to_options.merge(options) appName = options[:appName] || $appName From ee444d2700a5049418212dca05ffffa766c9fdbb Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Wed, 29 Apr 2020 10:39:29 +0300 Subject: [PATCH 3/3] fix indentation --- xcode/commonFastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcode/commonFastfile b/xcode/commonFastfile index 56d268e..1307e51 100644 --- a/xcode/commonFastfile +++ b/xcode/commonFastfile @@ -370,7 +370,7 @@ def set_xcconfig_for_configuration_of_project(xcconfig_name, configuration, xcod Xcodeproj::Constants::PRODUCT_TYPE_UTI[:app_extension] ] return !t.test_target_type? && supported_product_types.include?(t.product_type) -end + end application_targets = project.native_targets.select(&target_to_modify_selector)