forked from Ivasoft/mattermost-mobile
1026 lines
35 KiB
Ruby
1026 lines
35 KiB
Ruby
fastlane_version '2.71.0'
|
|
fastlane_require 'aws-sdk-s3'
|
|
fastlane_require 'erb'
|
|
fastlane_require 'json'
|
|
fastlane_require 'pathname'
|
|
|
|
skip_docs
|
|
|
|
configured = false
|
|
is_build_pr = false
|
|
|
|
# Executes before anything else use to setup the script
|
|
before_all do
|
|
ENV['SPACESHIP_SKIP_2FA_UPGRADE'] = '1'
|
|
|
|
is_build_pr = ENV['BUILD_PR'] == 'true'
|
|
if is_build_pr && ENV['CIRCLECI'] == 'true'
|
|
ENV['BRANCH_TO_BUILD'] = ENV['CIRCLE_BRANCH']
|
|
end
|
|
|
|
if ENV['CIRCLECI'] != 'true'
|
|
# 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'] || ENV['GIT_BRANCH']
|
|
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
|
|
end
|
|
|
|
after_all do |lane|
|
|
if ENV['CIRCLECI'] != 'true'
|
|
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
|
|
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?
|
|
package = load_config_json('../package.json')
|
|
package['version'] = version_number
|
|
save_json_as_file('../package.json', package)
|
|
|
|
lock = load_config_json('../package-lock.json')
|
|
lock['version'] = version_number
|
|
save_json_as_file('../package-lock.json', lock)
|
|
|
|
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 'Increments version and build number for both Android and iOS'
|
|
lane :set_app_version_build do
|
|
set_app_version
|
|
set_app_build_number
|
|
end
|
|
|
|
desc 'Configure the app before building'
|
|
lane :configure do
|
|
json = load_config_json('../dist/assets/config.json')
|
|
|
|
# Set the Segment API Key
|
|
unless ENV['RUDDER_API_KEY'].nil? || ENV['RUDDER_API_KEY'].empty?
|
|
json['RudderApiKey'] = ENV['RUDDER_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
|
|
|
|
# Configure show app review
|
|
if ENV['SHOW_REVIEW'] == 'true'
|
|
json['ShowReview'] = true
|
|
end
|
|
|
|
# Configure Show Onboarding
|
|
if ENV['SHOW_ONBOARDING'] == 'true'
|
|
json['ShowOnboarding'] = true
|
|
end
|
|
|
|
# Save the config.json file
|
|
save_json_as_file('../dist/assets/config.json', json)
|
|
|
|
configured = true
|
|
end
|
|
|
|
desc 'Upload file to s3'
|
|
lane :upload_file_to_s3 do |options|
|
|
os_type = options[:os_type].downcase
|
|
extension = os_type == "android" ? "*.apk" : "*.ipa"
|
|
build_folder_path = Dir[File.expand_path('..')].first
|
|
files = []
|
|
|
|
unless options[:file].nil? || options[:file].empty?
|
|
files.push("#{build_folder_path}/#{options[:file]}")
|
|
else
|
|
files = Dir.glob("#{build_folder_path}/#{extension}").select { |f| File.file? f}
|
|
end
|
|
|
|
unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty? || files.length == 0
|
|
version_number = ''
|
|
build_number = ''
|
|
pr_file = ''
|
|
plist_file = ''
|
|
|
|
if is_build_pr
|
|
pr_file = File.basename(files.first)
|
|
plist_file = "#{File.basename(files.first, '.*')}.plist"
|
|
else
|
|
version_number = os_type == "android" ?
|
|
android_get_version_name(gradle_file: './android/app/build.gradle') :
|
|
get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
|
|
build_number = os_type == "android" ?
|
|
android_get_version_code(gradle_file: './android/app/build.gradle') :
|
|
get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
|
|
end
|
|
|
|
s3_region = ENV['AWS_REGION']
|
|
s3_bucket_name = ENV['AWS_BUCKET_NAME']
|
|
s3_folder = is_build_pr ?
|
|
"#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}" :
|
|
"#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
|
|
|
|
s3 = Aws::S3::Resource.new(region: s3_region)
|
|
s3_bucket = s3.bucket(s3_bucket_name)
|
|
mutex = Mutex.new
|
|
threads = []
|
|
file_number = 0
|
|
total_files = files.length
|
|
links = []
|
|
total_files.times do |i|
|
|
threads[i] = Thread.new {
|
|
until files.empty?
|
|
mutex.synchronize do
|
|
file_number += 1
|
|
Thread.current["file_number"] = file_number
|
|
end
|
|
file = files.pop rescue nil
|
|
next unless file
|
|
|
|
filename = File.basename(file)
|
|
puts "[#{Thread.current["file_number"]}/#{total_files}] #{filename} uploading..."
|
|
s3_file = s3_bucket.object("#{s3_folder}/#{filename}")
|
|
s3_file.upload_file("#{file}")
|
|
links.push(s3_file.public_url)
|
|
end
|
|
}
|
|
end
|
|
threads.each { |t| t.join }
|
|
|
|
if is_build_pr
|
|
if os_type == 'android'
|
|
install_url = "https://#{s3_bucket_name}/#{s3_folder}/#{pr_file}"
|
|
elsif os_type == 'ios'
|
|
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.object("#{s3_folder}/#{plist_file}")
|
|
plist_obj.put(body: plist_body)
|
|
|
|
install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket_name}/#{s3_folder}/#{plist_file}"
|
|
end
|
|
|
|
qa_build_message({
|
|
:os_type => os_type,
|
|
:install_url => install_url
|
|
})
|
|
end
|
|
|
|
if options[:file] == 'Mattermost-simulator-x86_64.app.zip'
|
|
pretext = '#### New iOS build for VM/Simulator'
|
|
msg = "Download link: #{links.first}"
|
|
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
|
|
|
|
UI.message("S3 public paths:\n#{links.join("\n")}")
|
|
end
|
|
end
|
|
|
|
desc 'Create GitHub release'
|
|
lane :github do
|
|
tag = ENV['CIRCLE_TAG'] || ENV['TAG']
|
|
|
|
if tag
|
|
version = android_get_version_name(gradle_file: './android/app/build.gradle')
|
|
build = android_get_version_code(gradle_file: './android/app/build.gradle')
|
|
changelog = File.read("metadata/changelog")
|
|
android = [
|
|
" * [Mattermost arm64-v8a](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-arm64-v8a.apk)",
|
|
" * [Mattermost armeabi-v7a](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-armeabi-v7a.apk)",
|
|
" * [Mattermost Universal](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-universal.apk)",
|
|
" * [Mattermost x86](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-x86.apk)",
|
|
" * [Mattermost x86_64](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost-x86_64.apk)",
|
|
]
|
|
changelog.concat("* Android\n#{android.join("\n")}\n")
|
|
changelog.concat("* iOS\n * [Mattermost](https://releases.mattermost.com/mattermost-mobile/#{version}/#{build}/Mattermost.ipa)")
|
|
|
|
github_release = set_github_release(
|
|
repository_name: "mattermost/mattermost-mobile",
|
|
api_token: ENV["GITHUB_TOKEN"],
|
|
name: "Mobile Version #{version}",
|
|
tag_name: tag,
|
|
description: changelog,
|
|
upload_assets: ["./Mattermost-unsigned.ipa", "./Mattermost-unsigned.apk"],
|
|
is_draft: true
|
|
)
|
|
end
|
|
end
|
|
|
|
platform :ios do
|
|
before_all do
|
|
if ENV['CIRCLECI'] == 'true'
|
|
setup_circle_ci
|
|
end
|
|
end
|
|
|
|
desc 'Get iOS match profiles'
|
|
lane :certs do
|
|
if !ENV['IOS_API_KEY_ID'].nil? && !ENV['IOS_API_KEY_ID'].empty?
|
|
api_key = get_apple_api_key()
|
|
match(
|
|
api_key: api_key,
|
|
type: ENV['MATCH_TYPE'] || 'adhoc'
|
|
)
|
|
end
|
|
end
|
|
|
|
desc 'Build iOS app'
|
|
lane :build do
|
|
unless configured
|
|
configure
|
|
end
|
|
update_identifiers
|
|
replace_assets
|
|
build_ios
|
|
upload_file_to_s3({:os_type => "ios"})
|
|
end
|
|
|
|
desc 'Build an unsigned ipa'
|
|
lane :unsigned do
|
|
UI.success('Building unsigned iOS app')
|
|
|
|
ENV['APP_NAME'] = 'Mattermost'
|
|
ENV['REPLACE_ASSETS'] = 'true'
|
|
ENV['BUILD_FOR_RELEASE'] = 'true'
|
|
ENV['APP_SCHEME'] = 'mattermost'
|
|
|
|
update_identifiers
|
|
replace_assets
|
|
|
|
build_ios_unsigned_or_simulator(
|
|
more_xc_args: "-sdk iphoneos CODE_SIGNING_ALLOWED=NO",
|
|
rel_build_dir: "Release-iphoneos",
|
|
output_file: "Mattermost-unsigned.ipa",
|
|
)
|
|
end
|
|
|
|
lane :simulator do
|
|
UI.success('Building iOS app for simulator')
|
|
|
|
output_file = "Mattermost-simulator-x86_64.app.zip"
|
|
|
|
build_ios_unsigned_or_simulator(
|
|
more_xc_args: "-sdk iphonesimulator -arch x86_64",
|
|
rel_build_dir: "Release-iphonesimulator",
|
|
output_file: output_file,
|
|
)
|
|
|
|
upload_file_to_s3({
|
|
:os_type => "ios",
|
|
:file => output_file
|
|
})
|
|
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 notification service extension bundle identifier
|
|
notification_bundle_id = ENV['NOTIFICATION_SERVICE_IDENTIFIER'] || 'com.mattermost.rnbeta.NotificationService'
|
|
update_app_identifier(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
plist_path: 'NotificationService/Info.plist',
|
|
app_identifier: notification_bundle_id
|
|
)
|
|
|
|
find_replace_string(
|
|
path_to_file: './ios/Mattermost.xcodeproj/project.pbxproj',
|
|
old_string: 'com.mattermost.rnbeta.NotificationService',
|
|
new_string: notification_bundle_id
|
|
)
|
|
|
|
# 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
|
|
)
|
|
|
|
# Set the deep link prefix
|
|
app_scheme = ENV['APP_SCHEME'] || 'mattermost'
|
|
app_auth_scheme = ENV['BETA_BUILD'] == 'true' ? 'mmauthbeta' : 'mmauth'
|
|
update_info_plist(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
plist_path: 'Mattermost/Info.plist',
|
|
block: proc do |plist|
|
|
urlScheme = plist["CFBundleURLTypes"].find{|scheme| scheme["CFBundleURLName"] == "com.mattermost"}
|
|
urlScheme[:CFBundleURLSchemes] = [app_scheme, app_auth_scheme]
|
|
end
|
|
)
|
|
|
|
# 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]
|
|
)
|
|
|
|
update_info_plist(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
plist_path: 'Mattermost/Info.plist',
|
|
block: proc do |plist|
|
|
plist["AppGroupIdentifier"] = app_group_id
|
|
end
|
|
)
|
|
|
|
update_app_group_identifiers(
|
|
entitlements_file: './ios/NotificationService/NotificationService.entitlements',
|
|
app_group_identifiers: [app_group_id]
|
|
)
|
|
|
|
update_info_plist(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
plist_path: 'NotificationService/Info.plist',
|
|
block: proc do |plist|
|
|
plist["AppGroupIdentifier"] = app_group_id
|
|
end
|
|
)
|
|
|
|
update_app_group_identifiers(
|
|
entitlements_file: './ios/MattermostShare/MattermostShare.entitlements',
|
|
app_group_identifiers: [app_group_id]
|
|
)
|
|
|
|
update_info_plist(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
plist_path: 'MattermostShare/Info.plist',
|
|
block: proc do |plist|
|
|
plist["AppGroupIdentifier"] = app_group_id
|
|
end
|
|
)
|
|
|
|
# Sync the provisioning profiles using match
|
|
if ENV['SYNC_PROVISIONING_PROFILES'] == 'true'
|
|
if !ENV['MATCH_PASSWORD'].nil? && !ENV['MATCH_PASSWORD'].empty?
|
|
match(type: ENV['MATCH_TYPE'] || 'adhoc')
|
|
end
|
|
if !ENV['IOS_API_KEY_ID'].nil? && !ENV['IOS_API_KEY_ID'].empty?
|
|
api_key = get_apple_api_key()
|
|
match(
|
|
api_key: api_key,
|
|
type: ENV['MATCH_TYPE'] || 'adhoc'
|
|
)
|
|
end
|
|
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/LaunchScreen.storyboard ../ios/SplashScreenResource/LaunchScreen.storyboard'
|
|
sh 'cp -R ../dist/assets/release/splash_screen/ios/SplashBackground.imageset ../ios/Mattermost/Images.xcassets/'
|
|
sh 'cp -R ../dist/assets/release/splash_screen/ios/SplashIcon.imageset ../ios/Mattermost/Images.xcassets/'
|
|
end
|
|
end
|
|
|
|
lane :deploy do |options|
|
|
ipa_path = options[:file]
|
|
|
|
unless ipa_path.nil?
|
|
submit_to_testflight(ipa_path)
|
|
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 setup_code_signing
|
|
update_code_signing_settings(
|
|
use_automatic_signing: false,
|
|
path: './ios/Mattermost.xcodeproj'
|
|
)
|
|
|
|
ENV['MATCH_APP_IDENTIFIER'].split(',').each do |id|
|
|
target = 'Mattermost'
|
|
if id.include? 'NotificationService'
|
|
target = 'NotificationService'
|
|
elsif id.include? 'MattermostShare'
|
|
target = 'MattermostShare'
|
|
end
|
|
|
|
profile = "sigh_#{id}_#{ENV['MATCH_TYPE']}"
|
|
config_mode = ENV['BUILD_FOR_RELEASE'] == 'true' ? 'Release' : 'Debug'
|
|
|
|
update_project_provisioning(
|
|
xcodeproj: './ios/Mattermost.xcodeproj',
|
|
profile: ENV["#{profile}_profile-path"], # optional if you use sigh
|
|
target_filter: ".*#{target}$", # matches name or type of a target
|
|
build_configuration: config_mode,
|
|
code_signing_identity: 'iPhone Distribution' # optionally specify the codesigning identity
|
|
)
|
|
end
|
|
end
|
|
|
|
def build_ios
|
|
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
|
|
app_name_sub = app_name.gsub(" ", "_")
|
|
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']
|
|
|
|
# Need to add xcargs to only notification and
|
|
xcargs = ENV['SENTRY_ENABLED'] == 'true' ? "SENTRY_DSN_IOS='#{ENV['SENTRY_DSN_IOS']}' SENTRY_ENABLED='#{ENV['SENTRY_ENABLED']}'" : ''
|
|
|
|
setup_code_signing
|
|
|
|
gym(
|
|
clean: true,
|
|
scheme: 'Mattermost',
|
|
configuration: config_mode,
|
|
workspace: './ios/Mattermost.xcworkspace',
|
|
export_method: method,
|
|
skip_profile_detection: true,
|
|
output_name: "#{app_name_sub}.ipa",
|
|
export_xcargs: "-allowProvisioningUpdates",
|
|
export_options: {
|
|
signingStyle: 'manual',
|
|
iCloudContainerEnvironment: 'Production'
|
|
},
|
|
xcargs:xcargs
|
|
)
|
|
end
|
|
|
|
def build_ios_unsigned_or_simulator(more_xc_args:, rel_build_dir:, output_file:)
|
|
root_dir = Dir[File.expand_path('..')].first
|
|
ios_build_dir = "#{root_dir}/ios/Build/Products"
|
|
|
|
ENV['APP_NAME'] = 'Mattermost'
|
|
ENV['REPLACE_ASSETS'] = 'true'
|
|
ENV['BUILD_FOR_RELEASE'] = 'true'
|
|
ENV['APP_SCHEME'] = 'mattermost'
|
|
|
|
update_identifiers
|
|
replace_assets
|
|
|
|
sh "rm -rf #{ios_build_dir}/"
|
|
sh "cd #{root_dir}/ios && xcodebuild -workspace Mattermost.xcworkspace -scheme Mattermost -configuration Release -parallelizeTargets CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO SYMROOT='#{ios_build_dir}' #{more_xc_args} "
|
|
sh "cd #{ios_build_dir}/#{rel_build_dir} && zip -r #{output_file} Mattermost.app"
|
|
sh "mv #{ios_build_dir}/#{rel_build_dir}/#{output_file} #{root_dir}"
|
|
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
|
|
move_apk_to_root
|
|
upload_file_to_s3({:os_type => "android"})
|
|
end
|
|
|
|
desc 'Build an unsigned apk'
|
|
lane :unsigned do
|
|
UI.success('Building unsigned Android app')
|
|
|
|
ENV['APP_NAME'] = 'Mattermost'
|
|
ENV['REPLACE_ASSETS'] = 'true'
|
|
ENV['BUILD_FOR_RELEASE'] = 'true'
|
|
ENV['APP_SCHEME'] = 'mattermost'
|
|
|
|
update_identifiers
|
|
replace_assets
|
|
|
|
gradle(
|
|
task: 'assemble',
|
|
build_type: 'Unsigned',
|
|
project_dir: 'android/'
|
|
)
|
|
|
|
sh "mv #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]} #{Pathname.new(File.expand_path(File.dirname(__FILE__))).parent.to_s}/Mattermost-unsigned.apk"
|
|
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)
|
|
android_update_namespace(app_folder_name: 'android/app', namespace: package_id)
|
|
|
|
app_scheme = ENV['APP_SCHEME'] || 'mattermost'
|
|
find_replace_string(
|
|
path_to_file: "./android/app/src/main/AndroidManifest.xml",
|
|
old_string: 'scheme="mattermost"',
|
|
new_string: "scheme=\'#{app_scheme}\'"
|
|
)
|
|
|
|
if ENV['BETA_BUILD'] != 'true'
|
|
find_replace_string(
|
|
path_to_file: "./android/app/src/main/AndroidManifest.xml",
|
|
old_string: 'scheme="mmauthbeta"',
|
|
new_string: 'scheme="mmauth"',
|
|
)
|
|
end
|
|
|
|
helpers_dir = './android/app/src/main/java/com/mattermost/helpers/'
|
|
beta_dir = './android/app/src/main/java/com/mattermost/rnbeta/'
|
|
release_dir = "./android/app/src/main/java/#{package_id.gsub '.', '/'}/"
|
|
if release_dir != beta_dir
|
|
unless Dir.exist?(".#{release_dir}")
|
|
FileUtils.mkdir_p ".#{release_dir}"
|
|
end
|
|
|
|
sh "mv .#{beta_dir}* .#{release_dir}"
|
|
|
|
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(".#{release_dir}*.kt") 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
|
|
|
|
Dir.glob('../android/app/src/main/java/com/mattermost/helpers/*.java') do |item|
|
|
find_replace_string(
|
|
path_to_file: item[1..-1],
|
|
old_string: 'import com.mattermost.rnbeta.*;',
|
|
new_string: "import #{package_id}.*;"
|
|
)
|
|
end
|
|
|
|
Dir.glob('../android/app/src/main/java/com/mattermost/helpers/*.kt') do |item|
|
|
find_replace_string(
|
|
path_to_file: item[1..-1],
|
|
old_string: 'import com.mattermost.rnbeta.*',
|
|
new_string: "import #{package_id}.*"
|
|
)
|
|
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
|
|
|
|
lane :deploy do |options|
|
|
apk_path = options[:file]
|
|
|
|
unless apk_path.nil?
|
|
submit_to_google_play(apk_path)
|
|
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: 'app:assemble',
|
|
build_type: config_mode,
|
|
project_dir: 'android/',
|
|
properties: {
|
|
'separateApk' => ENV["SEPARATE_APKS"] || false,
|
|
'universalApk' => ENV["SEPARATE_APKS"] || false,
|
|
}
|
|
)
|
|
end
|
|
|
|
def move_apk_to_root
|
|
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
|
|
app_name_sub = app_name.gsub(" ", "_")
|
|
apks = lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS]
|
|
json_path = lane_context[SharedValues::GRADLE_OUTPUT_JSON_OUTPUT_PATH]
|
|
|
|
if json_path.nil? || json_path.empty?
|
|
dir_name = File.dirname(apks.first)
|
|
json_path = Dir.glob(File.join(dir_name, '*.json')).first
|
|
end
|
|
|
|
UI.message("JSON PATH APK #{json_path}")
|
|
outputJson = load_config_json(json_path)
|
|
paths = []
|
|
apks.each do |apk|
|
|
filename = File.basename(apk)
|
|
output = outputJson["elements"].find { |o| o["outputFile"] == filename }
|
|
unless output.nil?
|
|
filterName = output["filters"].empty? ? '' : output["filters"].first["value"]
|
|
name = "#{app_name_sub}"
|
|
unless filterName.nil? || filterName.empty?
|
|
name += "-#{filterName}"
|
|
end
|
|
new_apk_path = "#{Pathname.new(File.expand_path(File.dirname(__FILE__))).parent.to_s}/#{name}.apk"
|
|
paths.push(new_apk_path)
|
|
sh "mv #{apk} #{new_apk_path}"
|
|
UI.message("APK PATH \"#{new_apk_path}\", #{filterName}")
|
|
end
|
|
end
|
|
|
|
lane_context[SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS] = paths
|
|
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(json_path)
|
|
config_file = File.read(json_path)
|
|
JSON.parse(config_file)
|
|
end
|
|
|
|
def save_json_as_file(json_path, json)
|
|
File.open(json_path, 'w') do |f|
|
|
f.write(JSON.pretty_generate(json))
|
|
f.write("\n")
|
|
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://mattermost-plugin-media.s3.amazonaws.com/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
|
|
}]
|
|
},
|
|
success: options[:success]
|
|
)
|
|
end
|
|
end
|
|
|
|
def get_apple_api_key
|
|
api_key = ''
|
|
unless ENV['IOS_API_KEY_ID'].nil? || ENV['IOS_API_KEY_ID'].empty? ||
|
|
ENV['IOS_API_ISSUER_ID'].nil? || ENV['IOS_API_ISSUER_ID'].empty? ||
|
|
ENV['IOS_API_KEY'].nil? || ENV['IOS_API_KEY'].empty?
|
|
api_key_path = "#{ENV['IOS_API_KEY_ID']}.p8"
|
|
File.open("../#{api_key_path}", 'w') do |f|
|
|
key_string = ENV['IOS_API_KEY']
|
|
p8_array = key_string.split('\n')
|
|
p8_array.each_with_index do |value, index|
|
|
f.write(value)
|
|
f.write("\n") unless index == p8_array.length - 1
|
|
end
|
|
end
|
|
|
|
api_key = app_store_connect_api_key(
|
|
key_id: ENV['IOS_API_KEY_ID'],
|
|
issuer_id: ENV['IOS_API_ISSUER_ID'],
|
|
key_filepath: "./#{api_key_path}",
|
|
in_house: ENV['IOS_IN_HOUSE'] == 'true', # optional but may be required if using match/sigh
|
|
)
|
|
|
|
File.delete("../#{api_key_path}")
|
|
end
|
|
|
|
return api_key
|
|
end
|
|
|
|
def submit_to_testflight(ipa_path)
|
|
ipa = (Dir.glob(ipa_path).select { |f| File.file? f}).first
|
|
|
|
if !ipa.nil?
|
|
UI.success("ipa file #{ipa}")
|
|
api_key = get_apple_api_key()
|
|
unless api_key.empty?
|
|
pilot(
|
|
ipa: ipa,
|
|
api_key: api_key
|
|
)
|
|
else
|
|
UI.success("Uploading with Username / Password")
|
|
pilot(
|
|
ipa: ipa
|
|
)
|
|
end
|
|
else
|
|
UI.user_error! "ipa file does not exist #{ipa}"
|
|
return
|
|
end
|
|
|
|
filename = File.basename(ipa)
|
|
version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost')
|
|
build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj')
|
|
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
|
|
public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{filename}"
|
|
|
|
|
|
# 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.\nDownload link: #{public_link}"
|
|
|
|
if ENV['BETA_BUILD'] == 'true'
|
|
pretext = '#### New iOS beta published to TestFlight'
|
|
msg = "Sign up as a beta tester [here](https://testflight.apple.com/join/Q7Rx7K9P).\nDownload link: #{public_link}"
|
|
end
|
|
|
|
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
|
|
|
|
def submit_to_google_play(apk_path)
|
|
apks = Dir.glob(apk_path).select { |f| File.file? f}
|
|
filenames = apks.map {|f| File.basename(f)}
|
|
|
|
if(apks.length > 0)
|
|
unless ENV['SUPPLY_JSON_KEY'].nil? || ENV['SUPPLY_JSON_KEY'].empty?
|
|
supply(
|
|
package_name: ENV['MAIN_APP_IDENTIFIER'] || ENV['SUPPLY_PACKAGE_NAME'],
|
|
track: ENV['SUPPLY_TRACK'] || 'alpha',
|
|
apk_paths: apks
|
|
)
|
|
end
|
|
else
|
|
UI.user_error! "There are no apks in #{apk_path}"
|
|
return
|
|
end
|
|
|
|
version_number = android_get_version_name(gradle_file: './android/app/build.gradle')
|
|
build_number = android_get_version_code(gradle_file: './android/app/build.gradle')
|
|
s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}"
|
|
public_path = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}"
|
|
links = (filenames.map { |f| "* #{public_path}/#{f}"}).join("\n")
|
|
|
|
# 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.\nDownload links:\n#{links}"
|
|
|
|
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).\nDownload links:\n#{links}"
|
|
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
|
|
|
|
def qa_build_message(options)
|
|
abbreviated_commit_hash = last_git_commit[:abbreviated_commit_hash]
|
|
os_type = options[:os_type]
|
|
install_url = options[:install_url]
|
|
|
|
unless ENV['MATTERMOST_WEBHOOK_URL'].nil? || ENV['MATTERMOST_WEBHOOK_URL'].empty?
|
|
os_type = options[:os_type]
|
|
|
|
if os_type == 'android'
|
|
msg = "QA build [#{abbreviated_commit_hash}](https://github.com/mattermost/mattermost-mobile/commit/#{abbreviated_commit_hash}) — [Android APK Link](#{install_url})"
|
|
mattermost(message: msg, username: 'Fastlane', icon_url: 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300')
|
|
elsif os_type == 'ios'
|
|
msg = "QA build [#{abbreviated_commit_hash}](https://github.com/mattermost/mattermost-mobile/commit/#{abbreviated_commit_hash}) — [iOS pList Link](#{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')
|
|
end
|
|
else
|
|
UI.success("PR Built for #{os_type}: #{install_url}")
|
|
end
|
|
end
|