Skip to content

Instantly share code, notes, and snippets.

@trilliwon
Created February 6, 2025 03:22
Show Gist options
  • Select an option

  • Save trilliwon/d2bf3ffd53258ffe8213296bf08c823b to your computer and use it in GitHub Desktop.

Select an option

Save trilliwon/d2bf3ffd53258ffe8213296bf08c823b to your computer and use it in GitHub Desktop.
Sampe Fastlane
default_platform :ios
platform :ios do
desc "Send Slack message to channels or threads"
lane :send_slack_message do |options|
require 'slack-ruby-client'
Slack.configure do |config|
config.token = ENV['SLACK_API_TOKEN']
end
client = Slack::Web::Client.new
slack_channel = options.fetch(:slack_channel, "group_ios_notifications")
slack_thread_ts = options.fetch(:slack_thread_ts, nil)
release_notes = options.fetch(:release_notes, "-")
username = "Rocket"
icon_url = "https://ca.slack-edge.com/T0MB3T0CC-U06GX5MDDT9-e5a25100cce1-512"
# Prepare the blocks for the Slack message
blocks = options.fetch(:blocks, [
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "#{release_notes}"
}
}
])
# Send the message to Slack
client.chat_postMessage(
channel: slack_channel,
username: username,
icon_url: icon_url,
thread_ts: slack_thread_ts,
blocks: blocks.to_json
)
end
desc "Send Text Slack message to channels or threads"
lane :send_text_slack_message do |options|
text = options.fetch(:text, "Rocket is here!")
options[:blocks] = [
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "#{text}"
}
}
]
send_slack_message(options)
end
desc "Send Firebase deploy Slack message to channels or threads"
lane :send_firebase_deploy_slack_message do |options|
end
desc "Sign app"
private_lane :sign_app do |options|
# fetch appstore connect api key
api_key = app_store_connect_api_key(
key_id: ENV["APPSTORE_CONNECT_KEY_ID"],
issuer_id: ENV["APPSTORE_CONNECT_ISSUER_ID"],
key_content: ENV["APPSTORE_CONNECT_KEY_CONTENT"],
duration: 1200, # optional (maximum 1200)
in_house: false # optional but may be required if using match/sigh
)
get_certificates(
api_key: api_key,
output_path: "./build/certs"
)
# for Main App
get_provisioning_profile(
api_key: api_key,
adhoc: options.fetch(:adhoc, true),
app_identifier: "", # app identifier
username: "", # apple id
team_id: "", # apple team id
output_path: "./build/profiles"
)
# for Widget
get_provisioning_profile(
api_key: api_key,
adhoc: options.fetch(:adhoc, true),
app_identifier: "", # app identifier
username: "", # apple id
team_id: "", # apple team id
output_path: "./build/profiles"
)
# last line will retrun value
api_key
end
desc "Build app"
lane :build do |options|
# sign_app
sign_app
# set build configuration
# check the available build configurations or set default value
scheme = options.fetch(:scheme, "APP_NAME")
export_method = options.fetch(:export_method, "ad-hoc")
export_options = options.fetch(:export_options, { signingStyle: "manual" })
clean = options.fetch(:clean, true)
output_directory = options.fetch(:output_directory, "./build")
# build
gym(
scheme: scheme,
export_method: export_method,
export_options: export_options,
clean: clean,
output_directory: output_directory,
)
end
desc "Test app"
lane :test do
scan(
workspace: "APP_NAME.xcworkspace", # workspace name
configuration: 'Debug', # build configuration
devices: ["iPhone 15"], # devices
scheme: "APP_NAME", # scheme name
testplan: "All", # testplan name
clean: true, # clean
code_coverage: true, # code coverage
slack_url: ENV["SLACK_WEBHOOK_URL"], # slack webhook url
slack_channel: "#group_ios_notifications" # default slack channel
)
end
desc "Clean up all the build outputs"
private_lane :clean_up do
# clean up
sh("pwd && ls -al")
sh("rm -rf ./build")
end
before_all do
puts "---"
end
desc "Deliver metadata of a new version to the iTunes connect"
lane :deliver_metadata do
# sign_app
sign_app(adhoc: false)
deliver(
force: false,
precheck_include_in_app_purchases: false
)
# clean up
clean_up
end
desc "Increase the build number"
lane :increase_build_number do |options|
api_key = options.fetch(:api_key, nil)
build_num = latest_testflight_build_number(
api_key: api_key
)
increment_build_number(
build_number: build_num + 1,
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj"
)
end
desc "firebase_app_distribution"
lane :deploy_firebase do |options|
begin
build_number = Time.now.strftime("#{Time.now.month}%d%H%M")
increment_build_number(
build_number: build_number,
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj"
)
version_number = get_version_number(
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj",
target: "APP_NAME"
)
options[:text] = "🏁 *λΉŒλ“œ μ‹œμž‘! Version: #{version_number}(#{build_number})*"
send_text_slack_message(options)
# build
build(
scheme: "APP_NAME",
export_method: "ad-hoc",
export_options: { signingStyle: "manual" },
)
options[:text] = "βœ… λΉŒλ“œ μ™„λ£Œ! Firebase μ—…λ‘œλ“œ μ‹œμž‘"
send_text_slack_message(options)
last_commit = last_git_commit
author_email = last_commit[:author_email]
abbreviated_commit_hash = last_commit[:abbreviated_commit_hash]
branch_name = git_branch
commit_author = last_commit[:author]
release_notes = options.fetch(:release_notes, "New build uploaded to Firebase")
firebase_app_distribution(
app: ENV["GOOGLE_APP_ID"],
service_credentials_file: "fastlane/service_credentials_file.json",
ipa_path: "build/APP_NAME.ipa",
groups: "delightroom, ios-group, po-group, design-group, upo-group, shared-tester",
release_notes: "#{release_notes}\n\nBranch: #{branch_name}\nAuthor: #{commit_author}"
)
upload_symbols_to_crashlytics(dsym_path: "build/APP_NAME.app.dSYM.zip")
firebase_release_info = lane_context[SharedValues::FIREBASE_APP_DISTRO_RELEASE]
testingUri = firebase_release_info[:testingUri]
buildVersion = firebase_release_info[:buildVersion]
displayVersion = firebase_release_info[:displayVersion]
options[:blocks] = [
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "Successfully deployed to Firebase!"
}
},
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "<#{testingUri}|iOS Firebase #{displayVersion}(#{buildVersion})>"
}
},
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "Version: *#{displayVersion}(#{buildVersion})*"
}
},
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "Last Commit: \`#{abbreviated_commit_hash}\` by \`#{author_email}\`"
}
}
]
send_slack_message(options)
options[:slack_channel] = options.fetch(:share_slack_channel, "group_ios_notifications")
options[:slack_thread_ts] = nil
options[:blocks] = [
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "<#{testingUri}|iOS Firebase #{displayVersion}(#{buildVersion})>"
}
},
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "#{release_notes}"
}
}
]
send_slack_message(options)
rescue => e
options[:text] = "μ—λŸ¬ λ°œμƒ: #{e}"
send_text_slack_message(options)
raise
end
# clean up
clean_up
end
desc "Deploy to Testflight"
lane :deploy_testflight do |options|
begin
last_commit = last_git_commit
author_email = last_commit[:author_email]
branch_name = git_branch
release_notes = options.fetch(:release_notes, "New features and bug fixes")
groups = options.fetch(:groups, "members").strip.split(',').collect(&:strip)
# sign_app
api_key = sign_app(adhoc: false)
increase_build_number(api_key: api_key)
build_number = get_build_number(
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj"
)
version_number = get_version_number(
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj",
target: "APP_NAME"
)
options[:text] = "🏁 *λΉŒλ“œ μ‹œμž‘! Version: #{version_number}(#{build_number})*"
send_text_slack_message(options)
build_app(
scheme: "APP_NAME",
export_method: "app-store",
clean: true,
export_options: {
provisioningProfiles: {
"APP_NAME" => "match AppStore APP_NAME",
"APP_NAME.Widget" => "match AppStore APP_NAME.Widget"
}
}
)
options[:text] = "βœ… λΉŒλ“œ μ™„λ£Œ! TestFlight μ—…λ‘œλ“œ μ‹œμž‘"
send_text_slack_message(options)
upload_to_testflight(
beta_app_review_info: {
contact_email: "", # contact email
contact_first_name: "", # contact first name
contact_last_name: "", # contact last name
contact_phone: "", # contact phone
notes: "Thanks for reviewing :)"
},
api_key: api_key,
distribute_external: true,
groups: groups,
submit_beta_review: true,
changelog: "#{release_notes}\nBranch: #{branch_name}\nAuthor: #{author_email}"
)
testflight_link = "<https://testflight.apple.com/v1/app/1111111111|Testflight #{version_number}(#{build_number})>"
options[:text] = "βœ… Successfully uploaded to TestFlight!\n#{testflight_link}"
send_text_slack_message(options)
options[:slack_channel] = options.fetch(:share_slack_channel, "group_ios_notifications")
options[:slack_thread_ts] = nil
options[:blocks] = [
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "#{testflight_link}"
}
},
{
"type" => "section",
"text" => {
"type" => "mrkdwn",
"text" => "#{release_notes}"
}
}
]
send_slack_message(options)
rescue => e
options[:text] = "μ—λŸ¬ λ°œμƒ: #{e}"
send_text_slack_message(options)
raise
end
clean_up
end
desc "Deploy a new version to the App Store"
lane :release do |options|
begin
api_key = sign_app(adhoc: false)
increase_build_number(api_key: api_key)
build_number = get_build_number(
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj"
)
version_number = get_version_number(
xcodeproj: "./Modules/APP_NAME/APP_NAME.xcodeproj",
target: "APP_NAME"
)
options[:text] = "🏁 *λΉŒλ“œ μ‹œμž‘! Version: #{version_number}(#{build_number})*"
send_text_slack_message(options)
build_app(
scheme: "APP_NAME",
export_method: "app-store",
clean: true,
export_options: {
provisioningProfiles: {
"APP_NAME" => "match AppStore APP_NAME",
"APP_NAME.Widget" => "match AppStore APP_NAME.Widget"
}
}
)
options[:text] = "βœ… λΉŒλ“œ μ™„λ£Œ! AppStore μ—…λ‘œλ“œ μ‹œμž‘"
send_text_slack_message(options)
upload_to_app_store( # Upload to App Store
force: true,
submit_for_review: true,
automatic_release: true,
phased_release: true,
skip_binary_upload: false,
skip_screenshots: true,
skip_metadata: false,
precheck_include_in_app_purchases: false,
skip_app_version_update: false,
metadata_path: "./fastlane/metadata",
submission_information: {
add_id_info_uses_idfa: false,
},
api_key: api_key
)
options[:text] = "βœ… Successfully uploaded to AppStore! *Version: #{version_number}(#{build_number})*"
send_text_slack_message(options)
rescue => e
options[:text] = "μ—λŸ¬ λ°œμƒ: #{e}"
send_text_slack_message(options)
raise
end
# clean up
clean_up
end
desc "Deliver metadata"
lane :upload_metadata do |options|
begin
api_key = sign_app(adhoc: false)
upload_to_app_store( # Upload to App Store
force: true,
submit_for_review: false,
phased_release: true,
skip_binary_upload: true,
skip_screenshots: true,
skip_metadata: false,
precheck_include_in_app_purchases: false,
skip_app_version_update: true,
metadata_path: "./fastlane/metadata",
api_key: api_key
)
rescue => e
options[:text] = "μ—λŸ¬ λ°œμƒ: #{e}"
send_text_slack_message(options)
raise
end
# clean up
clean_up
end
desc "Deliver screenshots to the iTunes connect"
lane :deliver_screenshots do
# sign_app
sign_app
deliver(
force: false,
skip_binary_upload: true,
skip_metadata: true,
skip_screenshots: false
)
# clean up
clean_up
end
# You can define as many lanes as you want
after_all do |lane|
puts "Successfully deployed new App Update."
end
error do |lane, exception|
slack(
message: "Exception from `#{exception.class}`: `#{exception.message}`",
slack_url: ENV["SLACK_WEBHOOK_URL"],
success: false
)
UI.error("Exception from `#{exception.class}`: `#{exception.message}`")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment