Skip to content

Instantly share code, notes, and snippets.

@JofArnold
Last active July 5, 2021 09:21
Show Gist options
  • Save JofArnold/3c5845472f950957c233507d34e65ea2 to your computer and use it in GitHub Desktop.
Save JofArnold/3c5845472f950957c233507d34e65ea2 to your computer and use it in GitHub Desktop.
Fastfile for creating apps on App Store Connect as well as certs, building and uploading to Testflight
# --------------------------------------------------------------------------------
#
# About
#
# Here is a useful Fastfile for delivering a multiple varients (targets) of an app
# to, ultimately, Testflight.
# The project is called "cione" and has two schemes and two targets called
# "cione" and "citwo".
# --------------------------------------------------------------------------------
#
# Approach
#
# This appoach of explicitly directly passing all the arguments to "produce",
# "match", "gym" etc isn't "the" way of doing things (most people us an AppFile,
# Matchfile, etc) however it does make it very clear what's being passed and
# does make it more obviously amendable to manipulation in code.
#
# To keep this generic (without revealing too much personal information!) I'm using
# a file called "envvars" with the following values. Note: the MATCH_GIT_URL
# value almost certainly needs to be the ssh GitHub url (starts with "git@" if
# you are using this in CI). Don't use quotes in this file to handle spaces -
# you don't need it.
# ./fastlane/envvars
# TEAM_NAME=Your Team Name
# ITC_TEAM_NAME=Your Team Name
# TEAM_ID=YourTeamId
# [email protected]
# [email protected]:GitHubAccount/your-certs-repo.git
# --------------------------------------------------------------------------------
#
# How to use:
#
# All the lanes (apart from the last two) take an option "variant". So for instance
# if you want to "create" the app "cione" (i.e. not build but rather create an entry
# in ASC then you run
#
# bundle exec fastlane create_app variant:cione
#
# Once you've done this, you usually want to create the certs. So here we run
#
# bundle exec fastlane signing variant:cione
# bundle exec fastlane signing variant:citwo
#
# In theory this should not only create certs and profiles but set them in your
# project.
#
# You are then free to run one of the commands to deploy to Test Flight. I.e
#
# bundle exec fastlane release_cione
# bundle exec fastlane release_citwo
# --------------------------------------------------------------------------------
#
# Configuration
fastlane_require 'dotenv'
Dotenv.overload 'envvars'
# saves writing "ENV[SOME_CAPS_THING]" everwhere
env_vars = {
"team_name" => ENV["TEAM_NAME"],
"itc_team_name" => ENV["ITC_TEAM_NAME"],
"team_id" => ENV["TEAM_ID"],
"username" => ENV["USERNAME"],
"match_git_url" => ENV["MATCH_GIT_URL"]
}
cione_config = {
"scheme" => "cione",
"target" => "cione",
"app_identifier" => "com.jofarnold.cione",
"app_name" => "CI One",
"app_version" => "0.1.0",
"sku" => "JOF_ARNOLD_CIONE_001",
}
citwo_config = {
"scheme" => "citwo",
"target" => "citwo",
"app_identifier" => "com.jofarnold.citwo",
"app_name" => "CI Two",
"app_version" => "0.1.0",
"sku" => "JOF_ARNOLD_CITWO_001",
}
configs = {
"cione" => cione_config,
"citwo" => citwo_config
}
# Save us typing "ios" all the time
default_platform(:ios)
# --------------------------------------------------------------------------------
# Helpers
desc "Increment build number for specified variant"
lane :increment_build_number_for_variant do |options|
v = options[:variant]
c = configs[v]
t = c["target"]
increment_build_number_in_xcodeproj(target: t)
increment_build_number_in_plist(target: t)
end
desc "Test env file loads"
lane :test_env_file do
puts env_vars
end
# --------------------------------------------------------------------------------
# Release steps
desc "Create app on Developer Portal and App Store Connect for specified variant"
lane :create_app do |options|
v = options[:variant]
c = configs[v]
puts "Creating app on developer portal for variant '" + c["app_name"] + "'"
create_app_online( # aka "produce"
team_name: env_vars["team_name"],
itc_team_name: env_vars["itc_team_name"],
app_identifier: c["app_identifier"],
app_name: c["app_name"],
app_version: c["version"],
sku: c["sku"],
platform: "ios",
)
end
desc "Sync certificates (creating if they don't already exist) for specified variant"
lane :signing do |options|
v = options[:variant]
c = configs[v]
puts "Syncing certificates for variant '" + c["app_name"] + "'"
sync_code_signing( # aka "match"
storage_mode: "git",
username: env_vars["username"],
team_name: env_vars["team_name"],
team_id: env_vars["team_id"],
git_url: env_vars["match_git_url"],
git_branch: "main",
app_identifier: c["app_identifier"],
type: options[:type] || 'development',
readonly: is_ci
)
end
desc "Build specified variant"
lane :build do |options|
v = options[:variant]
c = configs[v]
signing(options)
increment_build_number_for_variant(options)
puts "Building app for variant '" + c["app_name"] + "'"
build_ios_app( # aka "gym aka "build_app"
export_team_id: env_vars["team_id"],
scheme: c["scheme"],
export_method: "app-store",
configuration: "Release",
output_directory: "build/ios",
export_options: {
provisioningProfiles: {
"com.jofarnold.cione" => "match AppStore com.jofarnold.cione", # You'll want to change these for your use case
"com.jofarnold.citwo" => "match AppStore com.jofarnold.citwo", # You'll want to change these for your use case
}
}
)
end
desc "Release specified variant"
lane :release do |options|
v = options[:variant]
c = configs[v]
build(options)
puts "Uploading app for variant '" + c["app_name"] + "'"
upload_to_app_store( # aka "deliver"
username: env_vars["username"],
team_name: env_vars["team_name"],
dev_portal_team_name: env_vars["itc_team_name"],
app_identifier: c["app_identifier"],
skip_screenshots: true,
skip_metadata: true
)
end
desc "Release CI One"
lane :release_cione do
options = {
"variant": "cione",
"type": "appstore"
}
release(options)
end
desc "Release CI Two"
lane :release_citwo do
options = {
"variant": "citwo",
"type": "appstore"
}
release(options)
end
source "https://rubygems.org"
gem "fastlane"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment