1# Customise this file, documentation can be found here: 2# https://github.com/KrauseFx/fastlane/tree/master/docs 3fastlane_require 'fileutils' 4fastlane_require 'shellwords' 5 6fastlane_version "2.141.0" 7default_platform :ios 8 9STAGING_API_URL = 'https://staging.exp.host/' 10 11# path relative to Fastfile 12output_directory = File.expand_path('./Deployment') 13 14def update_bundle_versions 15 bundle_short_version = Actions.lane_context[SharedValues::VERSION_NUMBER] 16 bundle_version = bundle_short_version + ".10" + number_of_commits.to_s 17 18 puts "Setting CFBundleVersion for Exponent to #{bundle_version}..." 19 set_info_plist_value( 20 path: "./ios/Exponent/Supporting/Info.plist", 21 key: "CFBundleVersion", 22 value: bundle_version 23 ) 24 25 puts "Setting CFBundleVersion to #{bundle_version} and CFBundleShortVersionString #{bundle_short_version} for ExpoNotificationServiceExtension..." 26 set_info_plist_value( 27 path: "./ios/ExpoNotificationServiceExtension/Info.plist", 28 key: "CFBundleVersion", 29 value: bundle_version 30 ) 31 32 set_info_plist_value( 33 path: "./ios/ExpoNotificationServiceExtension/Info.plist", 34 key: "CFBundleShortVersionString", 35 value: bundle_short_version 36 ) 37end 38 39# This will keep a copy of the original Info.plist 40@original_info_plist = nil 41@original_notification_extension_info_plist = nil 42 43def save_original_info_plists 44 update_info_plist( 45 xcodeproj: "ios/Exponent.xcodeproj", 46 plist_path: "Exponent/Supporting/Info.plist", 47 block: proc do |plist| 48 @original_info_plist = Marshal.load(Marshal.dump(plist)) 49 end 50 ) 51 52 update_info_plist( 53 xcodeproj: "ios/Exponent.xcodeproj", 54 plist_path: "ExpoNotificationServiceExtension/Info.plist", 55 block: proc do |plist| 56 @original_notification_extension_info_plist = Marshal.load(Marshal.dump(plist)) 57 end 58 ) 59end 60 61def restore_original_info_plists 62 update_info_plist( 63 xcodeproj: "ios/Exponent.xcodeproj", 64 plist_path: "Exponent/Supporting/Info.plist", 65 block: proc do |plist| 66 puts "Restoring original Exponent/Info.plist..." 67 plist.replace(@original_info_plist) 68 end 69 ) 70 71 update_info_plist( 72 xcodeproj: "ios/Exponent.xcodeproj", 73 plist_path: "ExpoNotificationServiceExtension/Info.plist", 74 block: proc do |plist| 75 puts "Restoring original ExpoNotificationServiceExtension/Info.plist..." 76 plist.replace(@original_notification_extension_info_plist) 77 end 78 ) 79end 80 81def remove_background_location 82 update_info_plist( 83 xcodeproj: "ios/Exponent.xcodeproj", 84 plist_path: "Exponent/Supporting/Info.plist", 85 block: proc do |plist| 86 puts "Removing background location permission strings and background mode..." 87 88 puts "Deleting NSLocationAlwaysAndWhenInUseUsageDescription key..." 89 plist.delete("NSLocationAlwaysAndWhenInUseUsageDescription") 90 91 puts "Deleting NSLocationAlwaysUsageDescription key..." 92 plist.delete("NSLocationAlwaysUsageDescription") 93 94 puts "Deleting location from UIBackgroundModes..." 95 plist["UIBackgroundModes"].delete("location") 96 end 97 ) 98end 99 100def remove_background_audio 101 update_info_plist( 102 xcodeproj: "ios/Exponent.xcodeproj", 103 plist_path: "Exponent/Supporting/Info.plist", 104 block: proc do |plist| 105 puts "Removing background audio background mode..." 106 plist["UIBackgroundModes"].delete("audio") 107 end 108 ) 109end 110 111def remove_background_notification 112 update_info_plist( 113 xcodeproj: "ios/Exponent.xcodeproj", 114 plist_path: "Exponent/Supporting/Info.plist", 115 block: proc do |plist| 116 puts "Removing remote-notification from UIBackgroundModes..." 117 plist["UIBackgroundModes"].delete("remote-notification") 118 end 119 ) 120end 121 122def configure_for_app_store 123 puts "Configuring app for App Store submission..." 124 remove_background_location 125 remove_background_audio 126 remove_background_notification 127end 128 129platform :ios do 130 131 before_all do 132 FileUtils.mkdir_p(output_directory) 133 Actions.lane_context[SharedValues::VERSION_NUMBER] = get_version_number( 134 xcodeproj: "./ios/Exponent.xcodeproj", 135 target: "Expo Go (versioned)" 136 ) 137 save_original_info_plists 138 update_bundle_versions 139 setup_circle_ci 140 end 141 142 lane :prepare_schemes do |options| 143 recreate_schemes( 144 project: "ios/Pods/#{options[:pod]}.xcodeproj" 145 ) 146 end 147 148 lane :test_module do |options| 149 scan( 150 project: "ios/Pods/#{options[:pod]}.xcodeproj", 151 # This scheme is autogenerated by CocoaPods when a testspec is included in the Podfile 152 scheme: "#{options[:pod]}-Unit-Tests", 153 clean: false, 154 ) 155 end 156 157 lane :test do 158 scan( 159 workspace: "ios/Exponent.xcworkspace", 160 scheme: "Exponent", 161 devices: ["iPhone SE", "iPhone X"], 162 clean: false, 163 ) 164 end 165 166 lane :create_simulator_build do |options| 167 directory = "./ios/simulator-build" 168 puts "Build will be written to: " + File.expand_path(directory) 169 170 flavor = options.fetch(:flavor, "versioned") 171 172 xcbuild( 173 workspace: "ios/Exponent.xcworkspace", 174 scheme: "Expo Go (#{flavor})", 175 sdk: "iphonesimulator", 176 configuration: "Release", 177 derivedDataPath: directory, 178 xcargs: "ARCHS=\"x86_64\" ONLY_ACTIVE_ARCH=NO -quiet", 179 raw_buildlog: is_ci? 180 ) 181 end 182 183 lane :create_expo_client_build do |options| 184 directory = "./ios" + prompt( 185 ci_input: "/../expo-client-build", 186 text: "Please enter the path where we should output the build, relative to ./ios (i.e. ../my-build):", 187 ) 188 puts "Build will be written to: " + File.expand_path(directory) 189 190 flavor = options.fetch(:flavor, "versioned") 191 192 xcbuild( 193 workspace: "ios/Exponent.xcworkspace", 194 scheme: "Expo Go (#{flavor})", 195 configuration: "Release", 196 derivedDataPath: directory, 197 sdk: "iphoneos", 198 destination: "generic/platform=iOS", 199 archive: true, 200 archive_path: directory + "/Exponent.xcarchive", 201 xcargs: "CODE_SIGNING_ALLOWED=NO APP_OWNER=Public -quiet" 202 ) 203 end 204 205 lane :release do |options| 206 cert( 207 team_id: "C8D8QTF339", 208 ) 209 210 sigh( 211 app_identifier: "host.exp.Exponent", 212 output_path: output_directory, 213 filename: "Exponent-distribution.mobileprovision", 214 ) 215 216 xcodebuild_args = { 217 APP_PROVISIONING_PROFILE: Actions.lane_context[Actions::SharedValues::SIGH_UDID], 218 } 219 xcodebuild_args = xcodebuild_args.map do |k,v| 220 "#{k.to_s.shellescape}=#{v.shellescape}" 221 end.join ' ' 222 223 configure_for_app_store 224 225 flavor = options.fetch(:flavor, "versioned") 226 227 gym( 228 workspace: "ios/Exponent.xcworkspace", 229 scheme: "Expo Go (#{flavor})", 230 configuration: "Release", 231 xcargs: xcodebuild_args, 232 clean: true, 233 output_directory: output_directory, 234 output_name: "Exponent.ipa", 235 ) 236 237 deliver( 238 team_id: "17102800", 239 ) 240 end 241 242 after_all do |lane| 243 # FileUtils.rm_rf(output_directory) 244 restore_original_info_plists 245 end 246 247 error do |lane, exception| 248 # slack( 249 # message: exception.message, 250 # success: false 251 # ) 252 end 253 254end 255 256platform :android do 257 258 private_lane :verify_changelog_exists do |version_code: | 259 changelog_path = "android/metadata/en-US/changelogs/#{version_code}.txt" 260 UI.user_error!("Missing changelog file at #{changelog_path}") unless File.exist?(changelog_path) 261 UI.message("Changelog exists for version code #{version_code}") 262 end 263 264 private_lane :verify_upload_to_staging do |version_name: | 265 require 'open-uri' 266 staging_version = nil 267 begin 268 versions = JSON.parse(open(URI.join(STAGING_API_URL, "/--/api/v2/versions")).read) 269 latest_major_version = versions['sdkVersions'].keys.map(&:to_i).sort().reverse().first() 270 latest_version = versions['sdkVersions']["#{latest_major_version}.0.0"] 271 staging_version = latest_version['androidClientVersion'] 272 rescue StandardError => e 273 UI.user_error!("Unable to validate androidClientVersion for latest SDK version on versions endpoint: #{e.inspect}. If this issue persists, remove the verify_upload_to_staging step from Fastfile and manually verify that the Android client has been deployed and tested on staging.") 274 end 275 276 unless staging_version == version_name 277 UI.user_error!("APK version #{version_name} is not yet uploaded to staging. Please download from the client_android CI job, test and upload manually.") 278 end 279 UI.message "APK version #{version_name} has been uploaded to staging" 280 end 281 282 lane :start do |flavor: "unversioned"| 283 gradle( 284 project_dir: "android", 285 task: "install", 286 build_type: "Debug", 287 flavor: flavor, 288 ) 289 adb(command: "shell am start -n host.exp.exponent/host.exp.exponent.LauncherActivity") 290 end 291 292 lane :build do |build_type: "Debug", flavor: "versioned", sign: true| 293 ENV['ANDROID_UNSIGNED'] = '1' unless sign 294 gradle( 295 project_dir: "android", 296 task: "app:assemble", 297 flavor: flavor, 298 build_type: build_type, 299 ) 300 end 301 302 lane :prod_release do 303 build_gradle = File.read("../android/app/build.gradle") 304 305 verify_changelog_exists(version_code: build_gradle.match(/versionCode (\d+)/)[1]) 306 verify_upload_to_staging(version_name: build_gradle.match(/versionName '([\d\.]+)'/)[1]) 307 308 supply( 309 package_name: "host.exp.exponent", 310 metadata_path: "./fastlane/android/metadata", 311 apk: "./android/app/build/outputs/apk/release/app-release.apk", 312 track: "production", 313 skip_upload_images: true, 314 skip_upload_screenshots: true 315 ) 316 end 317 318end 319