Files
iNaturalistReactNative/fastlane/Fastfile
Amanda Bullington 0e71fb5062 Patch draggable-flatlist with fix for reanimated 3 (#1346)
* Temporarily comment out Android in Fastfile build lane

* Fix: make draggable flatlist work with reanimated 3

* Add lib and commonjs library patch
2024-04-02 15:54:16 -07:00

459 lines
15 KiB
Ruby

# frozen_string_literal: true
require "fileutils"
require "spaceship"
appfile_path = File.join( __dir__, "Appfile" )
unless File.exist?( appfile_path )
UI.abort_with_message! <<~NO_APPFILE_ERROR.gsub( /\s+/, " " ).strip
Could not find #{appfile_path}. Copy the example file in that directory to
that path and fill in the relevant values to use Fastlane.
NO_APPFILE_ERROR
end
forbidden_env_vars = [
"MOCK_MODE"
]
forbidden_env_vars.each do | env_var |
next unless ENV[env_var].to_s.size.positive?
UI.abort_with_message! <<~NO_ENV_ERROR.gsub( /\s+/, " " ).strip
ENV is set #{env_var}. Remove the value from your ENV before running fastlane.
NO_ENV_ERROR
end
required_env_vars = [
"IOS_PROVISIONING_PROFILE_NAME",
"IOS_SHARE_BUNDLE_ID",
"IOS_SHARE_PROVISIONING_PROFILE_NAME"
]
required_env_vars.each do | env_var |
next unless ENV[env_var].to_s.size.zero?
UI.abort_with_message! <<~NO_ENV_ERROR.gsub( /\s+/, " " ).strip
ENV is missing #{env_var}. Add the value to .env or populate your ENV in
another way.
NO_ENV_ERROR
end
VERSION = File.open( "../package.json" ) {| f | JSON.parse( f.read )["version"] }
editor_cmd = [
ENV["EDITOR"],
`git config core.editor`,
`which vi`
].map {| e | e.to_s.strip }.detect {| e | !e.empty? }
if editor_cmd.nil?
UI.abort_with_message! <<~NO_EDITOR_ERROR
Could not find an editor, not even vi. Set the EDITOR environmental
variable or the core.editor git config"
NO_EDITOR_ERROR
end
editor_cmd_needs_to_wait = ( editor_cmd =~ /^code / || editor_cmd =~ /^subl / ) && !editor_cmd.include?( "-w" )
EDITOR = editor_cmd_needs_to_wait ? "#{editor_cmd} -w" : editor_cmd
XCODEPROJ = "ios/iNaturalistReactNative.xcodeproj"
PACKAGE_ID = CredentialsManager::AppfileConfig.try_fetch_value( :package_name )
IOS_BUNDLE_ID = CredentialsManager::AppfileConfig.try_fetch_value( :app_identifier )
IOS_ITC_TEAM_ID = CredentialsManager::AppfileConfig.try_fetch_value( :itc_team_id )
# https://github.com/fastlane/fastlane/issues/20741#issuecomment-1280687500
# ENV["ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD"] = "true"
def set_android_version_code( new_version_code )
build_gradle_path = "../android/app/build.gradle"
new_gradle = File.read( build_gradle_path ).sub( /versionCode\s+\d+/, "versionCode #{new_version_code}" )
File.open( build_gradle_path, "w" ) {| f | f.write( new_gradle ) }
end
def set_android_version_name( _new_version_name )
build_gradle_path = "../android/app/build.gradle"
new_gradle = File.read( build_gradle_path ).sub( /versionName\s+".+"/, "versionName \"#{VERSION}\"" )
File.open( build_gradle_path, "w" ) {| f | f.write( new_gradle ) }
end
def get_changelog_path( build_number = nil )
build_number ||= get_build_number( xcodeproj: XCODEPROJ )
changelog_dir_path = File.join( "metadata", "android", "en-US", "changelogs" )
FileUtils.mkpath( changelog_dir_path )
File.join( changelog_dir_path, "#{build_number}.txt" )
end
def get_aab_path( build_number = nil )
build_number ||= get_build_number( xcodeproj: XCODEPROJ )
File.expand_path( File.join(
__dir__,
"..",
"android",
"app",
"build",
"outputs",
"bundle",
"release",
"#{PACKAGE_ID}-v#{VERSION}+#{build_number}-release.aab"
) )
end
def get_apk_path( build_number = nil )
build_number ||= get_build_number( xcodeproj: XCODEPROJ )
File.expand_path( File.join(
__dir__,
"..",
"android",
"app",
"build",
"outputs",
"apk",
"release",
"#{PACKAGE_ID}-v#{VERSION}+#{build_number}-release.apk"
) )
end
def get_ipa_path( build_number = nil )
build_number ||= get_build_number( xcodeproj: XCODEPROJ )
File.expand_path( File.join(
__dir__,
"..",
"ios",
"build",
"#{PACKAGE_ID}-v#{VERSION}+#{build_number}.ipa"
) )
end
lane :tag do
desc "Add a new tag with an incremented version"
ensure_git_status_clean( ignore_files: ["fastlane/Fastfile"] )
last_tag = last_git_tag
# Increment the iOS build number
increment_build_number( xcodeproj: XCODEPROJ )
increment_version_number( xcodeproj: XCODEPROJ, version_number: VERSION )
build_number = get_build_number( xcodeproj: XCODEPROJ )
# set android/app/build.gradle versionCode to this build_number
set_android_version_code( build_number )
# set android/app/build.gradle versionName to VERSION
set_android_version_name( VERSION )
tag = "v#{VERSION}+#{build_number}"
changes = changelog_from_git_commits( pretty: "# * %h %s (%an, %ai)" )
if last_tag && changes.empty?
UI.abort_with_message! "Nothing has changed since the last tag (#{last_tag})"
end
# Get release notes
# Bit silly but takes advantage of existing syntax highlighting
fname = "COMMIT_EDITMSG"
File.open( fname, "w" ) do | f |
f << <<~INSTRUCTIONS
# Enter notes about what's new in #{tag}. Lines beginning with # will be ignored.
# Keep notes within the 500 character limit required by Google Play.
#
# Here's what changed since the last tag:
#{changes}
INSTRUCTIONS
end
system "#{EDITOR} #{fname}", exception: true
release_notes = ""
File.readlines( fname ).each do | line |
release_notes += line unless line[0] == "#"
end
release_notes.strip!
FileUtils.rm( fname )
if release_notes.strip.size.zero?
reset_git_repo skip_clean: true
UI.abort_with_message! "You gotta enter release notes!"
end
if release_notes.strip.length > 500
reset_git_repo skip_clean: true
UI.abort_with_message! "Release notes must be 500 characters or less"
end
# Write release notes to a place where they can be translated and add that file to git
changelog_path = get_changelog_path( build_number )
File.open( changelog_path, "w" ) do | f |
f << "#{release_notes}\n"
end
changelog_git_path = File.join( "fastlane", changelog_path )
git_add( path: changelog_git_path )
# commit
commit_version_bump(
message: tag,
xcodeproj: XCODEPROJ,
include: [
"android/app/build.gradle",
changelog_git_path
]
)
push_to_git_remote
# Create a tag for this release
add_git_tag( tag: tag )
push_git_tags
end
platform :android do
lane :build do
desc "Build release files for Android"
Dir.children( "../android/app/src/main/assets/camera" ).each do | file |
next if ENV.value?( file )
UI.abort_with_message! <<~EXTRA_ANDROID_ASSETS_ERROR
Android assets folder has extraneous file: #{file}, which is not listed
as an environment variable. Remove any unreferenced files from
android/app/src/main/assets/camera before running a release build
EXTRA_ANDROID_ASSETS_ERROR
end
assets = File.open( "../android/link-assets-manifest.json" ) {| f | JSON.parse( f.read )["data"] }
linked_assets = []
assets.each do | asset |
linked_assets.push( asset["path"].split( "assets/fonts/" )[1] )
end
Dir.children( "../assets/fonts" ).each do | file |
next unless !linked_assets.include?( file ) && ( file != ".DS_Store" )
UI.abort_with_message! <<~EXTRA_ASSETS_ERROR
Assets folder has extraneous file: #{file}, which is not listed
in link-assets-manifest.json. Remove any unreferenced assets before
running a release build
EXTRA_ASSETS_ERROR
end
keystore_properties_path = File.join(
__dir__,
"..",
"android",
"keystore.properties"
)
unless File.exist?( keystore_properties_path )
UI.abort_with_message! <<~NO_KEYSTORE_PROPERTIES_ERROR.gsub( /\s+/, " " ).strip
Could not find #{keystore_properties_path}. Copy the example file in that directory to
that path and fill in the relevant values to build for Android.
NO_KEYSTORE_PROPERTIES_ERROR
end
build_number = get_build_number( xcodeproj: XCODEPROJ )
# Build AAB. This should write
# android/app/build/outputs/bundle/release/PACKAGE_ID-vVERSION_NAME+VERSION_CODE-release.aab
aab_path = get_aab_path( build_number )
if File.exist?( aab_path )
UI.important "AAB already exists at #{aab_path}"
else
gradle( task: "bundle", project_dir: "android" )
end
unless File.exist?( aab_path )
UI.abort_with_message! "Failed to create AAB at #{aab_path}"
end
# Build APK. This should write
# android/app/build/outputs/apk/release/PACKAGE_ID-vVERSION_NAME+VERSION_CODE-release.apk
apk_path = get_apk_path( build_number )
if File.exist?( apk_path )
UI.important "APK already exists at #{apk_path}"
else
gradle( task: "build", project_dir: "android", flags: "-x lint" )
end
unless File.exist?( apk_path )
UI.abort_with_message! "Failed to create APK at #{apk_path}"
end
end
lane :clean do
sh "rm -rf ../android/build", step_name: "Deleting Android build folder"
sh "(cd ../android && ./gradlew clean)", step_name: "Cleaning Android build environment"
Dir.glob( File.join( File.dirname( get_aab_path ), "*.aab" ) ).each do | aab_path |
UI.message "Deleting #{aab_path}"
File.delete aab_path
end
Dir.glob( File.join( File.dirname( get_apk_path ), "*.apk" ) ).each do | apk_path |
UI.message "Deleting #{apk_path}"
File.delete apk_path
end
end
end
platform :ios do
lane :build do
desc "Build release files for iOS"
# Build iOS app
get_certificates
get_provisioning_profile
ipa_path = get_ipa_path
if File.exist?( ipa_path )
UI.important "IPA already exists at #{ipa_path}"
else
build_app(
workspace: File.join( "ios", "iNaturalistReactNative.xcworkspace" ),
scheme: "iNaturalistReactNative",
output_directory: File.dirname( ipa_path ),
output_name: File.basename( ipa_path ),
# I'd rather not do it this way, but so far I haven't figured out a
# way for fastlane to automatically detect both of the provisioning
# profiles we need
export_options: {
signingStyle: "manual",
provisioningProfiles: {
IOS_BUNDLE_ID => ENV["IOS_PROVISIONING_PROFILE_NAME"],
ENV["IOS_SHARE_BUNDLE_ID"] => ENV["IOS_SHARE_PROVISIONING_PROFILE_NAME"]
}
}
)
end
end
lane :clean do
sh "rm -rf ios/build", step_name: "Deleting iOS build folder"
Dir.glob( File.join( File.dirname( get_ipa_path ), "*.ipa" ) ).each do | ipa_path |
UI.message "Deleting #{ipa_path}"
File.delete ipa_path
end
end
end
lane :build do
desc "Build release files for all platforms"
Fastlane::LaneManager.cruise_lane "ios", "build"
# commenting out temporarily to avoid building in Android
# Fastlane::LaneManager.cruise_lane "android", "build"
end
lane :clean do
desc "Delete build artifacts"
sh "rm -rf $TMPDIR/react-*", step_name: "Deleting React Native cache"
sh "rm -rf $TMPDIR/metro-*", step_name: "Deleting Metro cache"
sh "watchman watch-del-all", step_name: "Deleting watchman cache"
Fastlane::LaneManager.cruise_lane "ios", "clean"
Fastlane::LaneManager.cruise_lane "android", "clean"
end
lane :release do
desc "Make github release for the latest tag and make builds"
last_tag = last_git_tag
if last_tag.nil? || last_tag.empty?
UI.abort_with_message! "No tags have been added yet. Try starting with `fastlane tag`"
end
original_branch = git_branch
system "git checkout #{last_tag}", exception: true
build_number = get_build_number( xcodeproj: XCODEPROJ )
if build_number.to_s != last_tag.split( "+" ).last
UI.abort_with_message! <<~MSG
The last tag doesn't match the current build number. Either make a new
tag or check out the tag before releasing.
MSG
end
changelog_path = get_changelog_path( build_number )
unless File.exist?( changelog_path )
UI.abort_with_message! <<~MSG
No change log file exists at #{changelog_path}. That should have been
created when you ran `fastlane tag`.
MSG
end
build
# commenting out temporarily to avoid building in Android
# apk_path = File.expand_path( get_apk_path( build_number ) )
# if File.exist?( apk_path )
# UI.success "Found APK at #{apk_path}"
# else
# UI.abort_with_message! "Failed to find APK at #{apk_path}"
# end
# github_release = get_github_release(
# url: "inaturalist/iNaturalistReactNative",
# version: last_tag,
# api_token: ENV["GITHUB_TOKEN"]
# )
# if github_release
# UI.important "Release already exists at #{github_release['url']}. You need to manually upload any missing assets."
# else
# set_github_release(
# repository_name: "inaturalist/iNaturalistReactNative",
# api_token: ENV["GITHUB_TOKEN"],
# name: last_tag,
# tag_name: last_tag,
# description: begin
# File.read( changelog_path )
# rescue StandardError
# nil
# end,
# # This is really just a fallback in case last_tag isn't really a tag
# commitish: "main",
# upload_assets: [apk_path]
# )
# end
system "git checkout #{original_branch}", exception: true
end
lane :internal do
desc "Push builds for the latest tag for internal testing"
# commenting out temporarily to avoid building in Android
# Ensure build files exist for the latest tag
# aab_path = get_aab_path
# unless File.exist?( aab_path )
# UI.abort_with_message! <<~MSG
# AAB does not exist at #{aab_path}. You may need to run the release lane before doing this.
# MSG
# end
last_tag = last_git_tag
if last_tag.nil? || last_tag.empty?
UI.abort_with_message! "No tags have been added yet. Try starting with `fastlane tag`"
end
changelog_path = get_changelog_path
unless File.exist?( changelog_path )
UI.abort_with_message! <<~MSG
No change log file exists at #{changelog_path}. That should have been
created when you ran `fastlane tag`.
MSG
end
# commenting out temporarily to avoid building in Android
# upload_to_play_store(
# aab: aab_path,
# track: "internal",
# version_name: last_tag
# )
upload_to_testflight(
ipa: get_ipa_path,
changelog: File.read( changelog_path ),
# https://github.com/fastlane/fastlane/issues/20756
itc_provider: CredentialsManager::AppfileConfig.try_fetch_value( :itc_provider ),
# The following distributes the build to our invite-only external test group
distribute_external: true,
groups: [
"iNat Friends"
]
)
end
lane :beta do
desc "Make latest builds available for public testing"
build_number = get_build_number( xcodeproj: XCODEPROJ )
UI.message "Play Store: promoting build #{build_number} to beta track"
upload_to_play_store(
version_code: build_number,
track: "internal",
track_promote_to: "beta"
)
UI.message "TestFlight: adding build #{build_number} to Open Beta group"
add_build_to_testflight_beta_group(
version_code: build_number,
beta_group: "Open Beta"
)
end
lane :prod do
desc "Push builds for the latest tag to production"
build_number = get_build_number( xcodeproj: XCODEPROJ )
# In theory this will move the release associated with the build_number in
# the beta track to the production track and 100% rollout... but I haven't
# been able to test that yet
upload_to_play_store(
version_code: build_number,
track: "beta",
track_promote_to: "production"
)
UI.abort_with_message! "This doesn't upload iOS builds yet"
end