Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[match][sigh][gym] migrate to new App Store Connect API and improve building with Catalyst #16158

Merged
merged 21 commits into from
Aug 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions fastlane/lib/fastlane/actions/sync_code_signing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ def self.define_provisioning_profile_mapping(params)
env_variable_name = Match::Utils.environment_variable_name_profile_name(app_identifier: app_identifier,
type: Match.profile_type_sym(params[:type]),
platform: params[:platform])

if params[:derive_catalyst_app_identifier]
app_identifier = "maccatalyst.#{app_identifier}"
joshdholtz marked this conversation as resolved.
Show resolved Hide resolved
end

mapping[app_identifier] = ENV[env_variable_name]
end

Expand Down
9 changes: 6 additions & 3 deletions gym/lib/gym/detect_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,13 @@ def self.min_xcode8?
# Is it an iOS device or a Mac?
def self.detect_platform
return if Gym.config[:destination]
platform = if Gym.project.mac? || Gym.building_mac_catalyst_for_mac?
min_xcode8? ? "macOS" : "OS X"
elsif Gym.project.tvos?

platform = if Gym.project.tvos?
"tvOS"
elsif Gym.building_for_ios?
"iOS"
elsif Gym.building_for_mac?
min_xcode8? ? "macOS" : "OS X"
else
"iOS"
end
Expand Down
22 changes: 22 additions & 0 deletions gym/lib/gym/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ def init_libs
require 'gym/xcodebuild_fixes/generic_archive_fix'
end

def building_for_ios?
if Gym.project.mac?
# Can be building for iOS if mac project and catalyst
return building_mac_catalyst_for_ios?
else
# Can be iOS project and build for mac if catalyst
return false if building_mac_catalyst_for_mac?

# Can be iOS project if iOS, tvOS, or watchOS
return Gym.project.ios? || Gym.project.tvos? || Gym.project.watchos?
end
end

def building_for_mac?
if Gym.project.supports_mac_catalyst?
# Can be a mac project and not build mac if catalyst
return building_mac_catalyst_for_mac?
else
return Gym.project.mac?
end
end

def building_mac_catalyst_for_ios?
Gym.project.supports_mac_catalyst? && Gym.config[:catalyst_platform] == "ios"
end
Expand Down
18 changes: 8 additions & 10 deletions gym/lib/gym/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
module Gym
class Runner
# @return (String) The path to the resulting ipa
# rubocop:disable Metrics/PerceivedComplexity
def run
unless Gym.config[:skip_build_archive]
build_app
Expand All @@ -24,12 +23,8 @@ def run

FileUtils.mkdir_p(File.expand_path(Gym.config[:output_directory]))

# Determine platform to archive
is_mac = Gym.project.mac? || Gym.building_mac_catalyst_for_mac?
is_ios = !is_mac && (Gym.project.ios? || Gym.project.tvos? || Gym.project.watchos?)

# Archive
if is_ios
if Gym.building_for_ios?
fix_generic_archive unless Gym.project.watchos? # See https://github.com/fastlane/fastlane/pull/4325
return BuildCommandGenerator.archive_path if Gym.config[:skip_package_ipa]

Expand All @@ -45,7 +40,7 @@ def run
move_asset_packs
move_appstore_info
end
elsif is_mac
elsif Gym.building_for_mac?
path = File.expand_path(Gym.config[:output_directory])
compress_and_move_dsym
if Gym.project.mac_app? || Gym.building_mac_catalyst_for_mac?
Expand Down Expand Up @@ -280,11 +275,14 @@ def copy_mac_app
app_path = File.join(BuildCommandGenerator.archive_path, "Products/Applications/#{exe_name}.app")

UI.crash!("Couldn't find application in '#{BuildCommandGenerator.archive_path}'") unless File.exist?(app_path)

joined_app_path = File.join(Gym.config[:output_directory], File.basename(app_path))
FileUtils.rm_rf(joined_app_path)
FileUtils.cp_r(app_path, File.expand_path(Gym.config[:output_directory]), remove_destination: true)
app_path = File.join(Gym.config[:output_directory], File.basename(app_path))

UI.success("Successfully exported the .app file:")
UI.message(app_path)
app_path
UI.message(joined_app_path)
joined_app_path
end

# Move the manifest.plist if exists into the output directory
Expand Down
6 changes: 6 additions & 0 deletions match/lib/match/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ def self.generate_certificate(params, cert_type, working_directory, specific_cer
specific_cert_type = cert_type.to_s
end

platform = params[:platform]
if platform.to_s == :catalyst.to_s
platform = :macos.to_s
end

arguments = FastlaneCore::Configuration.create(Cert::Options.available_options, {
platform: platform,
development: params[:type] == "development",
type: specific_cert_type,
generate_apple_certs: params[:generate_apple_certs],
Expand Down
9 changes: 7 additions & 2 deletions match/lib/match/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,19 @@ def self.available_options
FastlaneCore::ConfigItem.new(key: :platform,
short_option: '-o',
env_name: "MATCH_PLATFORM",
description: "Set the provisioning profile's platform to work with (i.e. ios, tvos, macos)",
description: "Set the provisioning profile's platform to work with (i.e. ios, tvos, macos, catalyst)",
default_value: default_platform,
default_value_dynamic: true,
verify_block: proc do |value|
value = value.to_s
pt = %w(tvos ios macos)
pt = %w(tvos ios macos catalyst)
UI.user_error!("Unsupported platform, must be: #{pt}") unless pt.include?(value)
end),
FastlaneCore::ConfigItem.new(key: :derive_catalyst_app_identifier,
env_name: "MATCH_DERIVE_CATALYST_APP_IDENTIFIER",
description: "Enable this if you have the Mac Catalyst capability enabled and your project was created with Xcode 11.3 or earlier. Prepends 'maccatalyst.' to the app identifier for the provisioning profile mapping",
type: Boolean,
default_value: false),
FastlaneCore::ConfigItem.new(key: :template_name,
env_name: "MATCH_PROVISIONING_PROFILE_TEMPLATE_NAME",
description: "The name of provisioning profile template. If the developer account has provisioning profile templates (aka: custom entitlements), the template name can be found by inspecting the Entitlements drop-down while creating/editing a provisioning profile (e.g. \"Apple Pay Pass Suppression Development\")",
Expand Down
17 changes: 12 additions & 5 deletions match/lib/match/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,20 +207,26 @@ def fetch_certificate(params: nil, working_directory: nil, specific_cert_type: n
return File.basename(cert_path).gsub(".cer", "") # Certificate ID
end

# rubocop:disable Metrics/PerceivedComplexity
# @return [String] The UUID of the provisioning profile so we can verify it with the Apple Developer Portal
def fetch_provisioning_profile(params: nil, certificate_id: nil, app_identifier: nil, working_directory: nil)
prov_type = Match.profile_type_sym(params[:type])

names = [Match::Generator.profile_type_name(prov_type), app_identifier]
if params[:platform].to_s == :tvos.to_s
if params[:platform].to_s == :tvos.to_s || params[:platform].to_s == :catalyst.to_s
names.push(params[:platform])
end

profile_name = names.join("_").gsub("*", '\*') # this is important, as it shouldn't be a wildcard
base_dir = File.join(prefixed_working_directory, "profiles", prov_type.to_s)

extension = params[:platform].to_s == :macos.to_s ? ".provisionprofile" : ".mobileprovision"
profiles = Dir[File.join(base_dir, "#{profile_name}#{extension}")]
extension = ".mobileprovision"
if [:macos.to_s, :catalyst.to_s].include?(params[:platform].to_s)
extension = ".provisionprofile"
end

profile_file = "#{profile_name}#{extension}"
profiles = Dir[File.join(base_dir, profile_file)]
if Helper.mac?
keychain_path = FastlaneCore::Helper.keychain_path(params[:keychain_name]) unless params[:keychain_name].nil?
end
Expand All @@ -242,7 +248,7 @@ def fetch_provisioning_profile(params: nil, certificate_id: nil, app_identifier:

if profile.nil? || force
if params[:readonly]
UI.error("No matching provisioning profiles found for '#{profile_name}'")
UI.error("No matching provisioning profiles found for '#{profile_file}'")
UI.error("A new one cannot be created because you enabled `readonly`")
if Dir.exist?(base_dir) # folder for `prov_type` does not exist on first match use for that type
all_profiles = Dir.entries(base_dir).reject { |f| f.start_with?(".") }
Expand Down Expand Up @@ -303,6 +309,7 @@ def fetch_provisioning_profile(params: nil, certificate_id: nil, app_identifier:

return uuid
end
# rubocop:enable Metrics/PerceivedComplexity

def device_count_different?(profile: nil, keychain_path: nil, platform: nil)
return false unless profile
Expand All @@ -320,7 +327,7 @@ def device_count_different?(profile: nil, keychain_path: nil, platform: nil)
Spaceship.device.all_ios_profile_devices.count
when :tvos
Spaceship.device.all_apple_tvs.count
when :mac
when :mac, :catalyst
Spaceship.device.all_macs.count
else
Spaceship.device.all.count
Expand Down
16 changes: 7 additions & 9 deletions match/lib/match/spaceship_ensure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def bundle_identifier_exists(username: nil, app_identifier: nil, platform: nil)
end

def certificates_exists(username: nil, certificate_ids: [], platform: nil)
if platform == :catalyst.to_s
platform = :macos.to_s
end

Spaceship.certificate.all(mac: platform == "macos").each do |cert|
certificate_ids.delete(cert.id)
end
Expand All @@ -61,18 +65,12 @@ def certificates_exists(username: nil, certificate_ids: [], platform: nil)
end

def profile_exists(username: nil, uuid: nil, platform: nil)
is_mac = platform == "macos"
found = Spaceship.provisioning_profile.all(mac: is_mac).find do |profile|
# App Store Connect API does not allow filter of profile by platform or uuid (as of 2020-07-30)
# Need to fetch all profiles and search for uuid on client side
found = Spaceship::ConnectAPI::Profile.all.find do |profile|
joshdholtz marked this conversation as resolved.
Show resolved Hide resolved
profile.uuid == uuid
end

# Look for iOS after looking for macOS (needed for Catalyst apps)
if !found && is_mac
found = Spaceship.provisioning_profile.all(mac: false).find do |profile|
profile.uuid == uuid
end
end

unless found
UI.error("Provisioning profile '#{uuid}' is not available on the Developer Portal for the user #{username}, fixing this now for you 🔨")
return false
Expand Down
69 changes: 42 additions & 27 deletions sigh/lib/sigh/download_all.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'spaceship'

require 'base64'

require_relative 'manager'
require_relative 'module'

Expand All @@ -12,35 +14,43 @@ def download_all(download_xcode_profiles: false)
Spaceship.select_team
UI.message("Successfully logged in")

if download_xcode_profiles
UI.deprecated("The App Store Connect API does not support querying for Xcode managed profiles: --download_code_profiles is deprecated")
joshdholtz marked this conversation as resolved.
Show resolved Hide resolved
end

case Sigh.config[:platform].to_s
when 'ios'
joshdholtz marked this conversation as resolved.
Show resolved Hide resolved
download_profiles(Spaceship.provisioning_profile.all(xcode: download_xcode_profiles))
xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: true)
profile_types = [
Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC,
Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT
Comment on lines +24 to +27
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to a common spot?

]
when 'macos'
download_profiles(Spaceship.provisioning_profile.all(mac: true, xcode: download_xcode_profiles))
xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: true)
profile_types = [
Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT
]
when 'catalyst'
profile_types = [
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
]
when 'tvos'
download_profiles(Spaceship.provisioning_profile.all_tvos)
xcode_profiles_downloaded?(xcode: download_xcode_profiles, supported: false)
profile_types = [
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT
Comment on lines +43 to +46
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to a common spot?

]
end
end

# @param xcode [Bool] Whether or not the user passed the download_xcode_profiles flag
# @param supported [Bool] Whether or not this platform supports downloading xcode profiles at all
def xcode_profiles_downloaded?(xcode: false, supported: false)
if supported
if xcode
UI.message("This run also included all Xcode managed provisioning profiles, as you used the `--download_xcode_profiles` flag")
elsif !xcode
UI.message("All Xcode managed provisioning profiles were ignored on this, to include them use the `--download_xcode_profiles` flag")
end

elsif !supported
if xcode
UI.important("Downloading Xcode managed profiles is not supported for platform #{Sigh.config[:platform]}")
return
end
end
# Filtering on 'profileType' seems to be undocumented as of 2020-07-30
# but works on both web session and official API
profiles = Spaceship::ConnectAPI::Profile.all(filter: { profileType: profile_types.join(",") }, includes: "bundleId")
download_profiles(profiles)
end

# @param profiles [Array] Array of all the provisioning profiles we want to download
Expand All @@ -57,26 +67,31 @@ def download_profiles(profiles)
end
end

def pretty_type(profile_type)
return Sigh.profile_pretty_type(profile_type)
end

# @param profile [ProvisioningProfile] A profile we plan to download and store
def download_profile(profile)
FileUtils.mkdir_p(Sigh.config[:output_path])

type_name = profile.class.pretty_type
profile_name = "#{type_name}_#{profile.uuid}_#{profile.app.bundle_id}"
type_name = pretty_type(profile.profile_type)
profile_name = "#{type_name}_#{profile.uuid}_#{profile.bundle_id.identifier}"

if Sigh.config[:platform].to_s == 'tvos'
profile_name += "_tvos"
end

if Sigh.config[:platform].to_s == 'macos'
if ['macos', 'catalyst'].include?(Sigh.config[:platform].to_s)
profile_name += '.provisionprofile'
else
profile_name += '.mobileprovision'
end

output_path = File.join(Sigh.config[:output_path], profile_name)
File.open(output_path, "wb") do |f|
f.write(profile.download)
content = Base64.decode64(profile.profile_content)
f.write(content)
end

Manager.install_profile(output_path) unless Sigh.config[:skip_install]
Expand Down
26 changes: 26 additions & 0 deletions sigh/lib/sigh/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ module Sigh
# Use this to just setup the configuration attribute and set it later somewhere else
class << self
attr_accessor :config

def profile_pretty_type(profile_type)
require 'spaceship'

case profile_type
when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_DEVELOPMENT,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DEVELOPMENT,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_DEVELOPMENT,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DEVELOPMENT
"Development"
when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_STORE,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_STORE
"AppStore"
when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_ADHOC,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_ADHOC
"AdHoc"
when Spaceship::ConnectAPI::Profile::ProfileType::IOS_APP_INHOUSE,
Spaceship::ConnectAPI::Profile::ProfileType::TVOS_APP_INHOUSE
"InHouse"
when Spaceship::ConnectAPI::Profile::ProfileType::MAC_APP_DIRECT,
Spaceship::ConnectAPI::Profile::ProfileType::MAC_CATALYST_APP_DIRECT
"Direct"
end
end
end

Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
Expand Down
4 changes: 2 additions & 2 deletions sigh/lib/sigh/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ def self.available_options
FastlaneCore::ConfigItem.new(key: :platform,
short_option: '-p',
env_name: "SIGH_PLATFORM",
description: "Set the provisioning profile's platform (i.e. ios, tvos)",
description: "Set the provisioning profile's platform (i.e. ios, tvos, macos, catalyst)",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no watchOS?????

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope

is_string: false,
default_value: "ios",
verify_block: proc do |value|
value = value.to_s
pt = %w(macos tvos ios)
pt = %w(macos tvos ios catalyst)
UI.user_error!("Unsupported platform, must be: #{pt}") unless pt.include?(value)
end),
FastlaneCore::ConfigItem.new(key: :readonly,
Expand Down
Loading