xref: /expo/fastlane/Fastfile (revision ab22f034)
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