mattermost-mobile/fastlane/Fastfile
Elias Nahum 3feaa8e6bb
iOS Native Share Extension (Swift) (#2575)
* iOS Native Share Extension (Swift)

* Re-arrange files

* Fix .gitignore
2019-02-26 14:31:57 -03:00

657 lines
21 KiB
Ruby

fastlane_version '2.71.0'
fastlane_require 'aws-sdk-s3'
fastlane_require 'erb'
fastlane_require 'json'
skip_docs
configured = false
# Executes before anything else use to setup the script
before_all do |lane, options|
if lane.to_s == 'build_pr'
pr = options[:pr]
UI.success("Building #{pr}")
ENV['BRANCH_TO_BUILD'] = pr
end
# Raises an error is git is not clean
if ENV['COMMIT_CHANGES_TO_GIT'] == 'true'
ensure_git_status_clean
end
# Block to ensure we are on the right branch
branch = ENV['BRANCH_TO_BUILD'] || 'master'
begin
ensure_git_branch(
branch: branch
)
rescue
sh "git checkout #{branch}"
UI.success("Using branch \"#{branch}\"")
end
# If we are going to commit changes to git create a separate branch
# so no changes are done in the branch that is being built
if ENV['COMMIT_CHANGES_TO_GIT'] == 'true'
local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build'
sh "git checkout -b #{local_branch}"
UI.success("Creating branch \"#{local_branch}\"")
end
end
after_all do |lane|
if lane.to_s == 'build'
submit_to_store
elsif lane.to_s == 'build_pr'
upload_to_s3
end
if ENV['RESET_GIT_BRANCH'] == 'true'
branch = ENV['BRANCH_TO_BUILD'] || 'master'
package_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta'
beta_dir = '../android/app/src/main/java/com/mattermost/rnbeta'
release_dir = "../android/app/src/main/java/#{package_id.gsub '.', '/'}"
if beta_dir != release_dir
sh "rm -rf #{release_dir}"
end
reset_git_repo(
force: true,
skip_clean: true
)
sh "git checkout #{branch}"
if ENV['COMMIT_CHANGES_TO_GIT'] == 'true'
local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build'
sh "git branch -D #{local_branch}"
UI.success("Deleted working branch \"#{local_branch}\"")
if lane.to_s == 'build_pr'
sh 'git checkout master'
## Remove the branch for the PR
sh "git branch -D #{branch}"
UI.success("Deleted PR branch \"#{branch}\"")
end
end
end
end
desc 'Increment version number for both Android and iOS'
lane :set_app_version do
version_number = ENV['VERSION_NUMBER']
unless version_number.nil? || version_number.empty?
android_set_version_name(
gradle_file: './android/app/build.gradle',
version_name: version_number
)
increment_version_number(
xcodeproj: './ios/Mattermost.xcodeproj',
version_number: version_number
)
if ENV['COMMIT_CHANGES_TO_GIT'] == 'true'
msg = ENV['INCREMENT_VERSION_NUMBER_MESSAGE'] || 'Bump app version number to '
commit_message = "#{msg} #{version_number.to_s}"
build_folder_path = Dir[File.expand_path('..')].first
repo_path = (sh "git -C #{build_folder_path} rev-parse --show-toplevel").strip
git_dirty_files = (sh "git -C #{repo_path} diff --name-only HEAD").split(" ").join(' ')
unless git_dirty_files.empty?
sh "git -C #{repo_path} commit -m \"#{commit_message}\" #{git_dirty_files}"
UI.success("Successfully committed \"#{git_dirty_files}\" 💾.")
end
end
end
end
desc 'Increments the build number for both Android and iOS'
lane :set_app_build_number do
## set the build number for both platforms
# use the one for iOS if no env variable set
if ENV['INCREMENT_BUILD_NUMBER'] === 'true'
build_number = ENV['BUILD_NUMBER'] || (get_build_number(xcodeproj: './ios/Mattermost.xcodeproj').to_i + 1)
increment_build_number(
xcodeproj: './ios/Mattermost.xcodeproj',
build_number: build_number
)
android_set_version_code(
gradle_file: './android/app/build.gradle',
version_code: build_number
)
if ENV['COMMIT_CHANGES_TO_GIT'] == 'true'
msg = ENV['INCREMENT_BUILD_NUMBER_MESSAGE'] || 'Bump app build number to '
commit_message = "#{msg} #{build_number.to_s}"
build_folder_path = Dir[File.expand_path('..')].first
repo_path = (sh "git -C #{build_folder_path} rev-parse --show-toplevel").strip
git_dirty_files = (sh "git -C #{repo_path} diff --name-only HEAD").split(" ").join(' ')
unless git_dirty_files.empty?
sh "git -C #{repo_path} commit -m \"#{commit_message}\" #{git_dirty_files}"
UI.success("Successfully committed \"#{git_dirty_files}\" 💾.")
end
end
end
end
desc 'Configure the app before building'
lane :configure do
json = load_config_json
# Set the Segment API Key
unless ENV['SEGMENT_API_KEY'].nil? || ENV['SEGMENT_API_KEY'].empty?
json['SegmentApiKey'] = ENV['SEGMENT_API_KEY']
end
# Configure Sentry if enabled
if ENV['SENTRY_ENABLED'] == 'true'
json['SentryEnabled'] = true
json['SentryOrg'] = ENV['SENTRY_ORG']
json['SentryProjectIos'] = ENV['SENTRY_PROJECT_IOS']
json['SentryProjectAndroid'] = ENV['SENTRY_PROJECT_ANDROID']
json['SentryDsnIos'] = ENV['SENTRY_DSN_IOS']
json['SentryDsnAndroid'] = ENV['SENTRY_DSN_ANDROID']
end
# Save the config.json file
save_config_json(json)
configured = true
end
desc 'Build the app for Android and iOS'
lane :build do |options|
configure
set_app_version
set_app_build_number
# Build the android app
self.runner.current_platform = :android
build
# Build the ios app
self.runner.current_platform = :ios
build
end
desc 'Buid the app for Android and iOS unsigned'
lane :unsigned do
configure
# Build the android app
self.runner.current_platform = :android
unsigned
# Build the ios app
self.runner.current_platform = :ios
unsigned
end
desc 'Build the app using a PR so QA can test'
lane :build_pr do
configure
# Build the android app
self.runner.current_platform = :android
build
# Build the ios app
self.runner.current_platform = :ios
build
end
platform :ios do
desc 'Build iOS app'
lane :build do
unless configured
configure
end
update_identifiers
replace_assets
build_ios
end
desc 'Build an unsigned ipa'
lane :unsigned do
unless configured
configure
end
update_identifiers
replace_assets
end
lane :update_identifiers do
# Set the name for the app
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
update_info_plist(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'Mattermost/Info.plist',
display_name: app_name
)
# Set the share extension bundle identifier
extension_bundle_id = ENV['EXTENSION_APP_IDENTIFIER'] || 'com.mattermost.rnbeta.MattermostShare'
update_app_identifier(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'MattermostShare/Info.plist',
app_identifier: extension_bundle_id
)
find_replace_string(
path_to_file: './ios/Mattermost.xcodeproj/project.pbxproj',
old_string: 'com.mattermost.rnbeta.MattermostShare',
new_string: extension_bundle_id
)
# Set the app bundle id
app_bundle_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta'
update_app_identifier(
xcodeproj: './ios/Mattermost.xcodeproj',
plist_path: 'Mattermost/Info.plist',
app_identifier: app_bundle_id
)
find_replace_string(
path_to_file: './ios/Mattermost.xcodeproj/project.pbxproj',
old_string: 'com.mattermost.rnbeta',
new_string: app_bundle_id
)
# If set update the development team
unless ENV['FASTLANE_TEAM_ID'].nil? || ENV['FASTLANE_TEAM_ID'].empty?
update_project_team(
path: './ios/Mattermost.xcodeproj',
teamid: ENV['FASTLANE_TEAM_ID']
)
end
# Set the ICloud container
icloud_container = ENV['IOS_ICLOUD_CONTAINER'] || 'iCloud.com.mattermost.rnbeta'
update_icloud_container_identifiers(
entitlements_file: './ios/Mattermost/Mattermost.entitlements',
icloud_container_identifiers: [icloud_container]
)
# Set the app group id to share data between the app and the extension
app_group_id = ENV['IOS_APP_GROUP'] || 'group.com.mattermost.rnbeta'
update_app_group_identifiers(
entitlements_file: './ios/Mattermost/Mattermost.entitlements',
app_group_identifiers: [app_group_id]
)
find_replace_string(
path_to_file: './ios/UploadAttachments/UploadAttachments/Constants.m',
old_string: 'group.com.mattermost.rnbeta',
new_string: app_group_id
)
update_app_group_identifiers(
entitlements_file: './ios/MattermostShare/MattermostShare.entitlements',
app_group_identifiers: [app_group_id]
)
# Save changes to the config.json file
config_json = load_config_json
save_config_json(config_json)
# Sync the provisioning profiles using match
if ENV['SYNC_PROVISIONING_PROFILES'] == 'true'
match(type: ENV['MATCH_TYPE'] || 'adhoc')
end
end
lane :replace_assets do
if ENV['REPLACE_ASSETS'] == 'true'
sh 'cp -R ../dist/assets/release/icons/ios/* ../ios/Mattermost/Images.xcassets/AppIcon.appiconset/'
sh 'cp -R ../dist/assets/release/splash_screen/ios/* ../ios/SplashScreenResource/'
end
end
error do |lane, exception|
version = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
send_message_to_mattermost({
:version_number => version,
:build_number => build_number,
:pretext => '',
:title => 'Unsuccessful Build',
:thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png',
:msg => exception.message,
:default_payloads => [:lane],
:success => false
})
end
def build_ios()
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
config_mode = ENV['BUILD_FOR_RELEASE'] == 'true' ? 'Release' : 'Debug'
method = ENV['IOS_BUILD_EXPORT_METHOD'].nil? || ENV['IOS_BUILD_EXPORT_METHOD'].empty? ? 'ad-hoc' : ENV['IOS_BUILD_EXPORT_METHOD']
gym(
clean: true,
scheme: 'Mattermost',
configuration: config_mode,
workspace: './ios/Mattermost.xcworkspace',
export_method: method,
skip_profile_detection: true,
output_name: "#{app_name}.ipa",
export_options: {
signingStyle: 'manual',
iCloudContainerEnvironment: 'Production'
}
)
end
end
platform :android do
desc 'Build Android app'
lane :build do
unless configured
configure
end
update_identifiers
replace_assets
link_sentry_android
build_android
end
desc 'Build an unsigned apk'
lane :unsigned do
unless configured
configure
end
update_identifiers
replace_assets
gradle(
task: 'assemble',
build_type: 'Unsigned',
project_dir: 'android/'
)
end
lane :update_identifiers do
# Set the name for the app
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
sh "echo '#{app_name}' > ../fastlane/metadata/android/en-US/title.txt"
android_change_string_app_name(
newName: app_name,
stringsFile: './android/app/src/main/res/values/strings.xml'
)
package_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta'
android_change_package_identifier(newIdentifier: package_id, manifest: './android/app/src/main/AndroidManifest.xml')
android_update_application_id(app_folder_name: 'android/app', application_id: package_id)
beta_dir = './android/app/src/main/java/com/mattermost/rnbeta/'
release_dir = "./android/app/src/main/java/#{package_id.gsub '.', '/'}/"
if ENV['BUILD_FOR_RELEASE'] == 'true'
find_replace_string(
path_to_file: "#{beta_dir}MainApplication.java",
old_string: 'return BuildConfig.DEBUG;',
new_string: 'return false;'
)
end
if release_dir != beta_dir
unless Dir.exist?(".#{release_dir}")
FileUtils.mkdir_p ".#{release_dir}"
end
sh "mv .#{beta_dir}* .#{release_dir}"
find_replace_string(
path_to_file: './android/app/BUCK',
old_string: 'package com.mattermost.rnbeta;',
new_string: "package #{package_id};"
)
Dir.glob(".#{release_dir}*.java") do |item|
find_replace_string(
path_to_file: item[1..-1],
old_string: 'package com.mattermost.rnbeta;',
new_string: "package #{package_id};"
)
end
Dir.glob('../android/app/src/main/java/com/mattermost/share/*.java') do |item|
find_replace_string(
path_to_file: item[1..-1],
old_string: 'import com.mattermost.rnbeta.MainApplication;',
new_string: "import #{package_id}.MainApplication;"
)
end
end
end
lane :replace_assets do
if ENV['REPLACE_ASSETS'] == 'true'
sh 'cp -R ../dist/assets/release/icons/android/* ../android/app/src/main/res/'
sh 'cp -R ../dist/assets/release/splash_screen/android/* ../android/app/src/main/res/'
end
end
error do |lane, exception|
build_number = android_get_version_code(
gradle_file: './android/app/build.gradle'
)
version_number = android_get_version_name(
gradle_file: './android/app/build.gradle'
)
send_message_to_mattermost({
:version_number => version_number,
:build_number => build_number,
:pretext => '',
:title => 'Unsuccessful Build',
:thumb_url => 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300',
:msg => exception.message,
:default_payloads => [:lane],
:success => false,
})
end
def build_android()
config_mode = ENV['BUILD_FOR_RELEASE'] == 'true' ? 'Release' : 'Debug'
gradle(
task: 'assemble',
build_type: config_mode,
project_dir: 'android/'
)
end
def link_sentry_android
if ENV['SENTRY_ENABLED'] == 'true'
url = 'https://sentry.io'
File.open('../android/sentry.properties', 'w+') do |f|
UI.message('Creating sentry.properties from environment')
f.write(
"defaults.url=#{url}\n"\
"defaults.org=#{ENV['SENTRY_ORG']}\n"\
"defaults.project=#{ENV['SENTRY_PROJECT_ANDROID']}\n"\
"auth.token=#{ENV['SENTRY_AUTH_TOKEN']}\n"
)
end
else
UI.message('Not creating sentry.properties because Sentry is disabled')
end
end
end
def load_config_json
config_path = '../dist/assets/config.json'
config_file = File.read(config_path)
JSON.parse(config_file)
end
def save_config_json(json)
config_path = '../dist/assets/config.json'
File.open(config_path, 'w') do |f|
f.write(JSON.pretty_generate(json))
end
end
def send_message_to_mattermost(options)
unless ENV['MATTERMOST_WEBHOOK_URL'].nil? || ENV['MATTERMOST_WEBHOOK_URL'].empty?
mattermost(
pretext: options[:pretext],
message: options[:msg],
default_payloads: options[:default_payloads],
username: 'Fastlane',
icon_url: 'https://s3-eu-west-1.amazonaws.com/fastlane.tools/fastlane.png',
payload: {},
attachment_properties: {
title: options[:title],
thumb_url: options[:thumb_url],
fields: [{
title: 'Version',
value: options[:version_number],
short: true
},
{
title: 'Build Number',
value: options[:build_number],
short: true
},
{
title: 'Built by',
value: 'Jenkins',
short: true
}]
},
success: options[:success]
)
end
end
def submit_to_store
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH]
# Submit to Google Play if required
if !apk_path.nil? && ENV['SUBMIT_ANDROID_TO_GOOGLE_PLAY'] == 'true'
UI.success("apk file #{apk_path}")
supply(
track: ENV['SUPPLY_TRACK'] || 'alpha',
apk: apk_path
)
build_number = android_get_version_code(
gradle_file: './android/app/build.gradle'
)
version_number = android_get_version_name(
gradle_file: './android/app/build.gradle'
)
# Send a build message to Mattermost
pretext = '#### New Android released ready to be published'
msg = 'release has been cut and is on the Beta track ready to be published.'
if ENV['BETA_BUILD'] == 'true'
pretext = '#### New Android beta published to Google Play'
msg = 'Sign up as a beta tester [here](https://play.google.com/apps/testing/com.mattermost.rnbeta)'
end
send_message_to_mattermost({
:version_number => version_number,
:build_number => build_number,
:pretext => pretext,
:title => '',
:thumb_url => 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300',
:msg => msg,
:default_payloads => [],
:success => true,
})
end
# Submit to TestFlight if required
if !ipa_path.nil? && ENV['SUBMIT_IOS_TO_TESTFLIGHT'] == 'true'
UI.success("ipa file #{ipa_path}")
pilot
# Send a build message to Mattermost
pretext = '#### New iOS released ready to be published'
msg = 'release has been cut and is on TestFlight ready to be published.'
if ENV['BETA_BUILD'] == 'true'
pretext = '#### New iOS beta published to TestFlight'
msg = 'Sign up as a beta tester [here](https://mattermost-fastlane.herokuapp.com/)'
end
version = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
send_message_to_mattermost({
:version_number => version_number,
:build_number => build_number,
:pretext => pretext,
:title => '',
:thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png',
:msg => msg,
:default_payloads => [],
:success => true,
})
end
unless apk_path.nil?
sh "mv #{apk_path} \"../#{app_name}.apk\""
end
end
def upload_to_s3
unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty?
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH]
s3_region = ENV['AWS_REGION']
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}"
s3_bucket = ENV['AWS_BUCKET_NAME']
s3 = Aws::S3::Resource.new(region: s3_region)
abbreviated_commit_hash = last_git_commit[:abbreviated_commit_hash]
filename = ENV['BRANCH_TO_BUILD']
unless apk_path.nil?
apk_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{filename}.apk")
apk_obj.upload_file("#{apk_path}")
android_install_path = "https://#{s3_bucket}/#{s3_folder}/#{filename}.apk"
if ENV['MATTERMOST_WEBHOOK_URL'] && !ENV['MATTERMOST_WEBHOOK_URL'].nil? && !ENV['MATTERMOST_WEBHOOK_URL'].empty?
msg = "QA build [#{abbreviated_commit_hash}](https://github.com/mattermost/mattermost-mobile/commit/#{abbreviated_commit_hash}) — [Android APK Link](#{android_install_path})"
mattermost(message: msg, username: 'Fastlane', icon_url: 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300')
else
UI.success("PR Built for Android: #{android_install_path}")
end
end
unless ipa_path.nil?
current_build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
plist_template = File.read('plist.erb')
plist_body = ERB.new(plist_template).result(binding)
plist_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{filename}.plist")
plist_obj.put(body: plist_body)
ipa_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{filename}.ipa")
ipa_obj.upload_file("#{ipa_path}")
ios_install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket}/#{s3_folder}/#{filename}.plist"
unless ENV['MATTERMOST_WEBHOOK_URL'].nil? || ENV['MATTERMOST_WEBHOOK_URL'].empty?
msg = "QA build [#{abbreviated_commit_hash}](https://github.com/mattermost/mattermost-mobile/commit/#{abbreviated_commit_hash}) — [iOS pList Link](#{ios_install_url})"
mattermost(message: msg, username: 'Fastlane', icon_url: 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png')
else
UI.success("PR Built for iOS: #{ios_install_url}")
end
end
end
end