$appName = File.basename(Dir['../*.xcworkspace'].first, '.*') private_lane :beforeBuild do |options| appName = options[:appName] || $appName 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(platform: "iOS") end cocoapods( repo_update: false ) set_info_plist_value( path: "./#{appName}/Info.plist", key: "CFBundleVersion", value: options[:buildNumber] || 10000 ) end private_lane :afterBuild do |options| if options[:uploadToFabric] appName = options[:appName] || $appName token = sh("cat ../#{appName}.xcodeproj/project.pbxproj | grep 'Fabric/run' | awk '{print $4}' | tr -d '\\n'") secret = sh("printf `cat ../#{appName}.xcodeproj/project.pbxproj | grep 'Fabric/run' | awk '{print $5}' | sed 's/..$//'` | tr -d '\\n'") releaseNotesFile = "release-notes.txt" sh("touch ../#{releaseNotesFile}") crashlytics( ipa_path: "./#{appName}.ipa", crashlytics_path: "./Pods/Crashlytics/", api_token: token, build_secret: secret, notes_path: releaseNotesFile, groups: "touch-instinct" ) upload_symbols_to_crashlytics( dsym_path: "./#{appName}.app.dSYM.zip", api_token: token ) end if options[:uploadToAppStore] app_version = get_info_plist_value( path: "./#{appName}/Info.plist", key: "CFBundleShortVersionString" ) upload_to_app_store( username: options[:username] || options[:apple_id], app_identifier: options[:app_identifier], app_version: options[:app_version] || app_version, ipa: "#{appName}.ipa", build_number: options[:buildNumber], skip_metadata: true, team_id: options[:itc_team_id], dev_portal_team_id: options[:team_id] ) end end private_lane :buildConfiguration do |options| configuration = options[:configuration] || lane_context[SharedValues::LANE_NAME] method = configuration_type_from_lane_name(configuration) options[:type] = profile_type_from_configuration_type(method) options = merge_options_with_config_file(options) beforeBuild(options) appName = options[:appName] || $appName uploadToFabric = options[:uploadToFabric] icloudEnvironment = options[:iCloudContainerEnvironment] || "" exportOptions = icloudEnvironment.to_s.empty? ? {} : {iCloudContainerEnvironment: icloudEnvironment} exportOptions[:compileBitcode] = options[:compileBitcode] || false options = options.merge(get_keychain_options(options)) openKeychain(options) options[:type] = profile_type_from_configuration_type(method) syncCodeSigning(options) gym( clean: true, workspace: "./#{appName}.xcworkspace", scheme: appName, archive_path: "./", output_directory: "./", output_name: "#{appName}.ipa", configuration: configuration, export_method: method, export_options: exportOptions, skip_package_ipa: !uploadToFabric ) closeKeychain(options) afterBuild(options) 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: type, readonly: options[:readonly] || true, 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 private_lane :closeKeychain do |options| if is_ci? delete_keychain(name: options[:keychain_name]) 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 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