$appName = File.basename(Dir['../*.xcworkspace'].first, '.*') private_lane :installDependencies do |options| podsReposPath = File.expand_path "~/.cocoapods/repos/master/" lockFilePath = "#{podsReposPath}/.git/index.lock" # check if .lock file exists in pod repos - then remove all master repo if File.exists? lockFilePath sh("rm -rf #{podsReposPath}") end if File.exists? "../Cartfile" carthage(command: "bootstrap", platform: "iOS") end cocoapods( repo_update: false ) end private_lane :uploadToFabric do |options| token, secret = fabric_keys_from_xcodeproj(options[:xcodeproj_path]) releaseNotesFile = "release-notes.txt" sh("touch ../#{releaseNotesFile}") crashlytics( ipa_path: options[:ipa_path], crashlytics_path: "./Pods/Crashlytics/submit", api_token: token, build_secret: secret, notes_path: releaseNotesFile, groups: "touch-instinct" ) upload_symbols_to_crashlytics( dsym_path: options[:dsym_path], api_token: token ) end private_lane :uploadToAppStore do |options| upload_to_app_store( username: options[:username] || options[:apple_id], ipa: options[:ipa_path], force: true, # skip metainfo prompt skip_metadata: true, team_id: options[:itc_team_id], dev_portal_team_id: options[:team_id] ) end private_lane :buildConfiguration do |options| appName = options[:appName] || $appName options[:scheme] = appName configuration = options[:configuration] || lane_context[SharedValues::LANE_NAME] options = options.merge(make_options_for_lane_name(configuration)) options = merge_options_with_config_file(options) options = options.merge(get_keychain_options(options)) openKeychain(options) if is_ci increment_build_number( build_number: options[:buildNumber] ) end ipa_name = "#{appName}.ipa" options[:output_name] = ipa_name options[:ipa_path] = "./#{ipa_name}" options[:dsym_path] = "./#{appName}.app.dSYM.zip" options[:xcodeproj_path] = "../#{appName}.xcodeproj" options[:workspace] = "./#{appName}.xcworkspace" installDependencies(options) if !(options[:uploadToFabric] || options[:uploadToAppStore]) options = merge_options_with_config_file(options) options[:skip_package_ipa] = true syncCodeSigning(options) buildArchive(options) # check build failures and static analysis end if options[:uploadToFabric] options = merge_options_with_config_file(options) buildArchive(options) syncCodeSigning(options) uploadToFabric(options) end 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) buildArchive(options) uploadToAppStore(options) end end private_lane :buildArchive do |options| icloudEnvironment = options[:iCloudContainerEnvironment] || "" exportOptions = icloudEnvironment.to_s.empty? ? {} : {iCloudContainerEnvironment: icloudEnvironment} exportOptions[:compileBitcode] = options[:compileBitcode] || false gym( clean: true, workspace: options[:workspace], scheme: options[:scheme], archive_path: "./", output_directory: "./", output_name: options[:output_name], configuration: options[:configuration], export_method: options[:method], export_options: exportOptions, skip_package_ipa: options[:skip_package_ipa], include_symbols: options[:include_symbols] || false, include_bitcode: options[:compileBitcode] || false, ) end lane :СreatePushCertificate do |options| certificates_path = File.expand_path "../Certificates" Dir.mkdir(certificates_path) unless File.directory?(certificates_path) get_push_certificate( development: options[:development] || true, generate_p12: true, active_days_limit: 30, # create new certificate if old one will expire in 30 days save_private_key: false, app_identifier: options[:app_identifier], p12_password: "123", # empty password won't work with Pusher output_path: certificates_path ) 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", keychain_name: options[:keychain_name], keychain_password: options[:keychain_password], skip_docs: true, platform: "ios" ) end private_lane :openKeychain do |options| if is_ci? create_keychain( name: options[:keychain_name], password: options[:keychain_password], unlock: true ) else unlock_keychain( path: options[:keychain_name], password: options[:keychain_password] ) end end def get_keychain_options(options) keychain_name = options[:keychain_name] keychain_password = options[:keychain_password] if is_ci? keychain_name = keychain_name || "ci.keychain" keychain_password = keychain_password || "" else keychain_password = keychain_password || prompt( text: "Please enter your keychain password (account password): ", secure_text: true ) end 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 end def profile_type_from_configuration_type(configuration_type) configuration_type.gsub("-", "") # "app-store" => "appstore" end def make_options_for_lane_name(lane_name) method = configuration_type_from_lane_name(lane_name) return { :configuration => lane_name, :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 fabric_keys_from_xcodeproj(xcodeproj_path) fabric_keys_from_shell_script(sh("cat #{xcodeproj_path}/project.pbxproj | grep 'Fabric/run'")) end def fabric_keys_from_shell_script(shell_script_contents) shell_script_contents.partition('Fabric/run\" ').last.partition('";').first.split(" ") end