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 before_all do 131 FileUtils.mkdir_p(output_directory) 132 Actions.lane_context[SharedValues::VERSION_NUMBER] = get_version_number( 133 xcodeproj: "./ios/Exponent.xcodeproj", 134 target: "Expo Go (versioned)" 135 ) 136 save_original_info_plists 137 update_bundle_versions 138 setup_circle_ci 139 end 140 141 lane :sync_screenshots do 142 upload_to_app_store( 143 team_id: "17102800", 144 app_identifier: "host.exp.Exponent", 145 screenshots_path: "./fastlane/screenshots", 146 skip_binary_upload: true, 147 skip_metadata: true 148 ) 149 end 150 151 lane :prepare_schemes do |options| 152 recreate_schemes( 153 project: "ios/Pods/#{options[:pod]}.xcodeproj" 154 ) 155 end 156 157 lane :test_module do |options| 158 scan( 159 project: "ios/Pods/#{options[:pod]}.xcodeproj", 160 # This scheme is autogenerated by CocoaPods when a testspec is included in the Podfile 161 scheme: "#{options[:pod]}-Unit-#{options[:testSpecName]}", 162 clean: false, 163 ) 164 end 165 166 lane :unit_tests do |options| 167 workspace = "apps/native-tests/ios/NativeTests.xcworkspace" 168 169 generated_scheme = generate_test_scheme( 170 scheme_name: 'NativeTests', 171 workspace_path: workspace, 172 targets: options[:targets] 173 ) 174 175 run_tests( 176 workspace: workspace, 177 scheme: generated_scheme, 178 devices: ["iPhone 14 Pro"], 179 configuration: 'Debug', 180 clean: false, 181 skip_build: true, 182 prelaunch_simulator: true, 183 skip_detect_devices: true, 184 skip_package_dependencies_resolution: true, 185 derived_data_path: "/tmp/ExpoUnitTestsDerivedData", 186 xcargs: 'CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO', 187 ) 188 end 189 190 lane :test do 191 scan( 192 workspace: "ios/Exponent.xcworkspace", 193 scheme: "Exponent", 194 devices: ["iPhone SE", "iPhone X"], 195 clean: false, 196 ) 197 end 198 199 lane :create_simulator_build do |options| 200 directory = "./ios/simulator-build" 201 puts "Build will be written to: " + File.expand_path(directory) 202 203 flavor = options.fetch(:flavor, "versioned") 204 205 xcbuild( 206 workspace: "ios/Exponent.xcworkspace", 207 scheme: "Expo Go (#{flavor})", 208 sdk: "iphonesimulator", 209 configuration: "Release", 210 derivedDataPath: directory, 211 xcargs: "ARCHS=\"x86_64\" ONLY_ACTIVE_ARCH=NO -quiet", 212 raw_buildlog: is_ci? 213 ) 214 end 215 216 lane :create_expo_client_build do |options| 217 directory = "./ios" + prompt( 218 ci_input: "/../expo-client-build", 219 text: "Please enter the path where we should output the build, relative to ./ios (i.e. ../my-build):", 220 ) 221 puts "Build will be written to: " + File.expand_path(directory) 222 223 flavor = options.fetch(:flavor, "versioned") 224 225 xcbuild( 226 workspace: "ios/Exponent.xcworkspace", 227 scheme: "Expo Go (#{flavor})", 228 configuration: "Release", 229 derivedDataPath: directory, 230 sdk: "iphoneos", 231 destination: "generic/platform=iOS", 232 archive: true, 233 archive_path: directory + "/Exponent.xcarchive", 234 xcargs: "CODE_SIGNING_ALLOWED=NO APP_OWNER=Public -quiet" 235 ) 236 end 237 238 lane :release do |options| 239 cert( 240 team_id: "C8D8QTF339", 241 ) 242 243 sigh( 244 app_identifier: "host.exp.Exponent", 245 output_path: output_directory, 246 filename: "Exponent-distribution.mobileprovision", 247 ) 248 249 xcodebuild_args = { 250 APP_PROVISIONING_PROFILE: Actions.lane_context[Actions::SharedValues::SIGH_UDID], 251 } 252 xcodebuild_args = xcodebuild_args.map do |k,v| 253 "#{k.to_s.shellescape}=#{v.shellescape}" 254 end.join ' ' 255 256 configure_for_app_store 257 258 flavor = options.fetch(:flavor, "versioned") 259 260 gym( 261 workspace: "ios/Exponent.xcworkspace", 262 scheme: "Expo Go (#{flavor})", 263 configuration: "Release", 264 xcargs: xcodebuild_args, 265 clean: true, 266 output_directory: output_directory, 267 output_name: "Exponent.ipa", 268 ) 269 270 deliver( 271 team_id: "17102800", 272 ) 273 end 274 275 after_all do |lane| 276 # FileUtils.rm_rf(output_directory) 277 restore_original_info_plists 278 end 279 280 error do |lane, exception| 281 # slack( 282 # message: exception.message, 283 # success: false 284 # ) 285 end 286 287end 288 289platform :android do 290 291 private_lane :verify_changelog_exists do |version_code: | 292 changelog_path = "android/metadata/en-US/changelogs/#{version_code}.txt" 293 UI.user_error!("Missing changelog file at #{changelog_path}") unless File.exist?(changelog_path) 294 UI.message("Changelog exists for version code #{version_code}") 295 end 296 297 private_lane :verify_upload_to_staging do |version_name: | 298 UI.message "Skipping staging verification step" 299 # require 'open-uri' 300 # staging_version = nil 301 # begin 302 # versions = JSON.parse(open(URI.join(STAGING_API_URL, "/--/api/v2/versions")).read) 303 # latest_major_version = versions['sdkVersions'].keys.map(&:to_i).sort().reverse().first() 304 # latest_version = versions['sdkVersions']["#{latest_major_version}.0.0"] 305 # staging_version = latest_version['androidClientVersion'] 306 # rescue StandardError => e 307 # 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.") 308 # end 309 310 # unless staging_version == version_name 311 # 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.") 312 # end 313 # UI.message "APK version #{version_name} has been uploaded to staging" 314 end 315 316 lane :start do |flavor: "unversioned"| 317 gradle( 318 project_dir: "android", 319 task: "install", 320 build_type: "Debug", 321 flavor: flavor, 322 ) 323 adb(command: "shell am start -n host.exp.exponent/host.exp.exponent.LauncherActivity") 324 end 325 326 lane :build do |build_type: "Debug", flavor: "versioned", sign: true, aab: false| 327 ENV['ANDROID_UNSIGNED'] = '1' unless sign 328 gradle( 329 project_dir: "android", 330 task: aab ? "app:bundle": "app:assemble", 331 flavor: flavor, 332 build_type: build_type, 333 ) 334 end 335 336 lane :upload_crashlytics_symbols do |flavor: "versioned"| 337 gradle( 338 project_dir: "android", 339 task: "app:uploadCrashlyticsSymbolFile", 340 flavor: flavor, 341 build_type: "Release", 342 ) 343 end 344 345 lane :prod_release do 346 build_gradle = File.read("../android/app/build.gradle") 347 348 verify_changelog_exists(version_code: build_gradle.match(/versionCode (\d+)/)[1]) 349 verify_upload_to_staging(version_name: build_gradle.match(/versionName '([\d\.]+)'/)[1]) 350 351 supply( 352 package_name: "host.exp.exponent", 353 metadata_path: "./fastlane/android/metadata", 354 aab: "./android/app/build/outputs/bundle/versionedRelease/app-versioned-release.aab", 355 track: "production", 356 skip_upload_images: true, 357 skip_upload_screenshots: true 358 ) 359 end 360 361end 362