diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..48fee14 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "build_options_helper"] + path = xcode/config_generator/build_options_helper + url = https://github.com/petropavel13/build_options_helper diff --git a/android_hooks/pre-push b/android_hooks/pre-push deleted file mode 100755 index cc5cdce..0000000 --- a/android_hooks/pre-push +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -echo "Pre push static analysis" - -output=$(./gradlew staticAnalysis --daemon 2>&1) - -status=$? - -if [ "$status" = 0 ] ; then - exit 0 -else - build_date=$(date +'%Y-%m-%d_%T') - file_name="build_$build_date" - folder_name="./build_log/" - file_path="${folder_name}${file_name}" - mkdir -p "$folder_name" - echo "$output" >> "$file_path" - echo "Pre push static failed. Full log path: ${file_path}" - exit 1 -fi diff --git a/gradle/installGitHooks.gradle b/gradle/installGitHooks.gradle deleted file mode 100644 index 8f1ebc4..0000000 --- a/gradle/installGitHooks.gradle +++ /dev/null @@ -1,7 +0,0 @@ -task installGitHooks(type: Copy) { - from new File(buildScriptsDir, 'android_hooks') - into { new File(rootProject.rootDir, '.git/hooks') } - fileMode 0777 -} - -tasks.getByPath(':app:preBuild').dependsOn installGitHooks diff --git a/lint/lint.xml b/lint/lint.xml index c834f09..57a80e9 100644 --- a/lint/lint.xml +++ b/lint/lint.xml @@ -8,12 +8,13 @@ - - + + + diff --git a/proguard/rules/components.pro b/proguard/rules/components.pro index 830b721..5bf7020 100644 --- a/proguard/rules/components.pro +++ b/proguard/rules/components.pro @@ -1,2 +1,2 @@ # View controllers are created through reflection. --keep class ** extends ru.touchin.roboswag.components.navigation.viewcontrollers.ViewController { *; } +-keep class ** extends ru.touchin.roboswag.navigation_viewcontroller.viewcontrollers.ViewController { *; } diff --git a/xcode/.swiftlint.yml b/xcode/.swiftlint.yml index 4bbc84c..d502145 100644 --- a/xcode/.swiftlint.yml +++ b/xcode/.swiftlint.yml @@ -239,6 +239,18 @@ custom_rules: message: "Use сontentView instead of self for addSubview or addSubviews methods in cell." severity: error + redundant_type_annotation_bool: + name: "Redundant type annotation for Bool" + regex: '((var|let)) *\w+ *((: *Bool *=)|((\w| |<|>|:)*= *BehaviorRelay\( *value *:)) *((true)|(false))' + message: "Using a type annotation for Bool is redundant." + severity: error + + parameter_repetition: + name: "Parameter repetition" + regex: 'func ((\w+([A-Z]\w+))|(\w+)) *(<[^>]+>)? *\( *(?i)(\3|\4):' + message: "The parameter name is actually used in the function name. Use _ instead." + severity: error + # Rx unused_map_parameter: diff --git a/xcode/commonFastfile b/xcode/commonFastfile index 382a448..d903469 100644 --- a/xcode/commonFastfile +++ b/xcode/commonFastfile @@ -87,7 +87,10 @@ end private_lane :addShield do |options| buildNumber = options[:buildNumber] - buildDescription = options[:xcconfig_name].scan(/\p{Upper}/)[1..2].join + buildDescription = options[:xcconfig_name] # EnterpriseCustomerDev1WithoutSSLPinningRelease + .split(/(?=[A-Z])/) # -> ["Enterprise", "Customer", "Dev1", "Without", "S", "S", "L", "Pinning", "Release"] + .map { |v| v.gsub(/[[:lower:]]+/, "") }[1..2] # -> ["E", "C", "D1", "W", "S", "S", "L", "P", "R"] -> ["C", "D1"] + .join # -> "CD1" begin add_badge( @@ -108,11 +111,7 @@ private_lane :buildConfiguration do |options| options[:xcconfig_name] = lane_name configuration_type = Touchlane::ConfigurationType.from_lane_name(lane_name) - configuration = get_configuration_for_type(configuration_type.type) - - options = configuration.to_options - .merge(get_keychain_options(options)) - .merge(options) + options = fill_up_options_using_configuration_type(options, configuration_type) openKeychain(options) @@ -136,13 +135,13 @@ private_lane :buildConfiguration do |options| if !(options[:uploadToFabric] || options[:uploadToAppStore]) options[:skip_package_ipa] = true - syncCodeSigning(options) + sync_code_signing_using_options(options) buildArchive(options) # check build failures and static analysis end if options[:uploadToFabric] - syncCodeSigning(options) + sync_code_signing_using_options(options) addShield(options) buildArchive(options) @@ -153,7 +152,7 @@ private_lane :buildConfiguration do |options| options[:compileBitcode] = options[:compileBitcode].nil? ? true : options[:compileBitcode] options[:include_symbols] = options[:include_symbols].nil? ? true : options[:include_symbols] - syncCodeSigning(options) + sync_code_signing_using_options(options) buildArchive(options) uploadToAppStore(options) @@ -206,23 +205,11 @@ lane :createPushCertificate do |options| ) end -lane :syncCodeSigning do |options| - match( - app_identifier: options[:app_identifier], - username: options[:username] || options[:apple_id], - team_id: options[:team_id], - type: options[:type], - readonly: options[:readonly].nil? ? true : options[:readonly], - 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, - platform: "ios" - ) +lane :SyncCodeSigning do |options| + configuration_type = Touchlane::ConfigurationType.from_type(options[:type]) + options = fill_up_options_using_configuration_type(options, configuration_type) + + sync_code_signing_using_options(options) end lane :SyncSymbols do |options| @@ -346,6 +333,32 @@ lane :ManuallyUpdateCodeSigning do |options| end +def sync_code_signing_using_options(options) + match( + app_identifier: options[:app_identifier], + username: options[:username] || options[:apple_id], + team_id: options[:team_id], + type: options[:type], + readonly: options[:readonly].nil? ? true : options[:readonly], + 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, + platform: "ios" + ) +end + +def fill_up_options_using_configuration_type(options, configuration_type) + configuration = get_configuration_for_type(configuration_type.type) + + configuration.to_options + .merge(get_keychain_options(options)) + .merge(options) +end def get_keychain_options(options) keychain_name = options[:keychain_name] diff --git a/xcode/config_generator/build_options_helper b/xcode/config_generator/build_options_helper new file mode 160000 index 0000000..3f81529 --- /dev/null +++ b/xcode/config_generator/build_options_helper @@ -0,0 +1 @@ +Subproject commit 3f81529c1425a74f43fe84fe8befffd81a442cc7 diff --git a/xcode/config_generator/custom_settings.json b/xcode/config_generator/custom_settings.json deleted file mode 100644 index a190324..0000000 --- a/xcode/config_generator/custom_settings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "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/example_settings.yaml b/xcode/config_generator/example_settings.yaml new file mode 100644 index 0000000..608981a --- /dev/null +++ b/xcode/config_generator/example_settings.yaml @@ -0,0 +1,27 @@ +targets: + TestProject: + development: + PRODUCT_BUNDLE_IDENTIFIER: "ru.touchin.testproject" + PROVISIONING_PROFILE_SPECIFIER: "TestProjectDev" + CODE_SIGN_ENTITLEMENTS: "TestProject/Standard.entitlements" + enterprise: + PRODUCT_BUNDLE_IDENTIFIER: "com.touchin.testproject" + PROVISIONING_PROFILE_SPECIFIER: "TestProjectEnterprise" + CODE_SIGN_ENTITLEMENTS: "TestProject/Enterprise.entitlements" + appstore: + PRODUCT_BUNDLE_IDENTIFIER: "ru.customer.domain" + PROVISIONING_PROFILE_SPECIFIER: "TestProjectAppStore" + CODE_SIGN_ENTITLEMENTS: "TestProject/Production.entitlements" + +types: + development: + apple_id: "apple@touchin.ru" + team_id: "**********" + itc_team_id: "**********" + enterprise: + apple_id: "enterpriseapple@touchin.ru" + team_id: "**********" + appstore: + apple_id: "apple@touchin.ru" + team_id: "**********" + itc_team_id: "**********" \ No newline at end of file diff --git a/xcode/config_generator/gen_configurations.py b/xcode/config_generator/gen_configurations.py deleted file mode 100644 index 22845ed..0000000 --- a/xcode/config_generator/gen_configurations.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/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 old mode 100644 new mode 100755 index dbe8d84..64d6b8e --- a/xcode/config_generator/render_xcconfigs.rb +++ b/xcode/config_generator/render_xcconfigs.rb @@ -1,110 +1,152 @@ require 'json' require 'mustache' +require 'yaml' + +# Usage: render_xcconfigs.rb +# +# Result: Adds .xcconfig files to $configs_folder_name directory. +# Files are only being added and changed, not removed! +# It is recommended to remove old .xcconfig files before running this script. + # 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 +class String + def in_current_dir + "#{__dir__}/#{self}" + end +end + +# Input files paths +configurations_file_path = ARGV[0] +temp_configs_data_file_path = "configs_data.json".in_current_dir +generator_path = "build_options_helper/helper.py".in_current_dir +template_path = "target_xcconfig.mustache".in_current_dir +build_parameters_path = ARGV[1] || "build_parameters.yaml".in_current_dir + +# 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") +# Call python script and generate configs to config file +system("python #{generator_path} -bp #{build_parameters_path} -o #{__dir__} -r ios_build_settings -p ios") +# Open settings, configurations and template files +target_xcconfig_tempate = File.read(template_path) +$configurations = YAML.load(File.open(configurations_file_path)) +$config_types = $configurations["types"] -# open settings + template file -settings = JSON.load(File.open("custom_settings.json")) -target_xcconfig_tempate = File.read("target_xcconfig.mustache") +# Set global property +targets = $configurations["targets"] - -# set global property -targets = settings["targets"] - -# make tuple of key and value become mustache template element +# 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 +# Maps lane prefix to distribution type +def distribution_type_of(account_type) + case account_type + when "Standard" + "development" + when "Enterprise" + "enterprise" + when "AppStore" + "appstore" + else + raise "Error: Unsupported distribution type #{account_type}" + end +end + +# Fetch development team from build configuration +def fetch_development_team(development_team_key, distribution_type) + current_config = $config_types[distribution_type] + team_value = current_config["team_id"] 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 +# Return empty array or generated provisioning profile hash +def generate_provisioning_profile(provisioning_key, bundle_id, distribution_type) + case distribution_type + when "appstore" + app_store_profile = "match AppStore " + bundle_id + config_option(provisioning_key, app_store_profile) + else + 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) +def generate_google_service_info_plist_path(google_service_info_plist_key, target_name, distribution_type) + google_service_info_plist_path = target_name + "/Resources/" + + path_suffix = case distribution_type + when "development" + "Standard-GoogleService-Info.plist" + when "enterprise" + "Enterprise-GoogleService-Info.plist" + else + "AppStore-GoogleService-Info.plist" + end + + return config_option(google_service_info_plist_key, google_service_info_plist_path + path_suffix) end -# generate missing properties if needed -def generate_missing_properties(target_name, properties, account_type) +# Generate missing properties if needed +def generate_missing_properties(target_name, properties, distribution_type) result = [] development_team_key = "DEVELOPMENT_TEAM" provisioning_key = "PROVISIONING_PROFILE_SPECIFIER" + google_service_info_plist_key = "GOOGLE_SERVICE_INFO_PLIST_PATH" + bundle_id_key = "PRODUCT_BUNDLE_IDENTIFIER" - 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] + # Bundle_id_key should be among the properties (required by fastlane) + unless properties.key?(bundle_id_key) + raise "#{target_name}: Could not find #{bundle_id_key} for #{distribution_type}" end unless properties.key?(development_team_key) - result.append(generate_development_team(development_team_key, account_type)) + result.append(fetch_development_team(development_team_key, distribution_type)) end unless properties.key?(provisioning_key) - result.append(generate_provisioning_profile(provisioning_key, bundle_id, account_type)) + result.append(generate_provisioning_profile(provisioning_key, properties[bundle_id_key], distribution_type)) + end + + unless properties.key?(google_service_info_plist_key) + result.append(generate_google_service_info_plist_path(google_service_info_plist_key, target_name, distribution_type)) end return result end -# run through all target in project -targets.each do |target| +# Run through all target in project +targets.each do |target_name, target| - # need open everytime, because script make some changes only for this target - configs = JSON.load(File.open("configs_data.json"))["configurations"] + # Need open everytime, because script make some changes only for this target + configs = JSON.load(File.open(temp_configs_data_file_path)) - # run through all configs + # 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] + # Take default values + distribution_type = distribution_type_of(config["account_type"]) + properties = target[distribution_type] - # add properties from settings file + # 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)) + # Add missing properties if needed + config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, distribution_type)) - # create settings pack + # Create settings pack config_data = { "target_name": target_name, "configuration": config } - # create file for every setting in loop + # 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)) } @@ -112,5 +154,5 @@ targets.each do |target| end -# remove config file, it's trash -File.delete("configs_data.json") if File.exist?("configs_data.json") +# Remove config file, it's trash +File.delete(temp_configs_data_file_path) if File.exist?(temp_configs_data_file_path) diff --git a/xcode/fastlane/touchlane/lib/configuration.rb b/xcode/fastlane/touchlane/lib/configuration.rb index a12c764..02cc4d3 100644 --- a/xcode/fastlane/touchlane/lib/configuration.rb +++ b/xcode/fastlane/touchlane/lib/configuration.rb @@ -13,18 +13,19 @@ module Touchlane 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] + configuration_hash = load_configuration_from_file(path) + attrs_hash = configuration_hash["types"][type] + identifiers = get_app_identifiers_from_configuration_hash(configuration_hash, 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"]) + new(config_type, identifiers, attrs_hash["apple_id"], attrs_hash["team_id"], attrs_hash["itc_team_id"]) end end - def self.load_hash_of_types_from_file(path) + def self.load_configuration_from_file(path) unless File.exists? path raise "Unable to load configurations from file at #{path}" else @@ -32,7 +33,14 @@ module Touchlane end end - private_class_method :new, :load_hash_of_types_from_file + def self.get_app_identifiers_from_configuration_hash(configuration_hash, type) + identifier_key = "PRODUCT_BUNDLE_IDENTIFIER" + configuration_hash["targets"].collect do |target, types| + types[type][identifier_key] or raise "#{target}: There is no #{identifier_key} field in #{type}" + end + end + + private_class_method :new, :load_configuration_from_file, :get_app_identifiers_from_configuration_hash def to_options {