Files
yattee/fastlane/Fastfile
Arkadiusz Fal f3f45fdfdc Fix mac notarized build archiving wrong platform
build_mac_app in the build_and_notarize lane was missing the sdk and
destination overrides needed for the multiplatform scheme, so gym
archived for tvOS and failed on provisioning profiles. Mirror the
working mac beta lane.
2026-07-18 18:00:57 +02:00

370 lines
11 KiB
Ruby

# Yattee v2 Fastlane configuration
# Single unified "Yattee" scheme, platform selected via destination parameter
APP_NAME = ENV['APP_NAME'] || 'Yattee'
DEVELOPER_KEY_ID = ENV['DEVELOPER_KEY_ID']
DEVELOPER_KEY_ISSUER_ID = ENV['DEVELOPER_KEY_ISSUER_ID']
DEVELOPER_KEY_CONTENT = ENV['DEVELOPER_KEY_CONTENT']
TEAM_ID = ENV['TEAM_ID']
TEMP_KEYCHAIN_USER = ENV['TEMP_KEYCHAIN_USER']
TEMP_KEYCHAIN_PASSWORD = ENV['TEMP_KEYCHAIN_PASSWORD']
DEVELOPER_APP_IDENTIFIER = ENV['DEVELOPER_APP_IDENTIFIER']
GIT_AUTHORIZATION = ENV['GIT_AUTHORIZATION']
TESTFLIGHT_EXTERNAL_GROUPS = ENV['TESTFLIGHT_EXTERNAL_GROUPS']
XCODEPROJ = "#{APP_NAME}.xcodeproj"
SCHEME = APP_NAME
def delete_temp_keychain(name)
delete_keychain(
name: name
) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end
def create_temp_keychain(name, password)
create_keychain(
name: name,
password: password,
unlock: false,
timeout: 0
)
end
def ensure_temp_keychain(name, password)
delete_temp_keychain(name)
create_temp_keychain(name, password)
end
add_extra_platforms(platforms: [:tvos])
before_all do
# Skipping update_fastlane in CI for stability
end
desc "Get latest TestFlight build number across all platforms"
lane :latest_build_number do
api_key = app_store_connect_api_key(
key_id: DEVELOPER_KEY_ID,
issuer_id: DEVELOPER_KEY_ISSUER_ID,
key_content: DEVELOPER_KEY_CONTENT,
is_key_content_base64: true
)
ios_build = latest_testflight_build_number(api_key: api_key, app_identifier: DEVELOPER_APP_IDENTIFIER, platform: "ios")
tvos_build = latest_testflight_build_number(api_key: api_key, app_identifier: DEVELOPER_APP_IDENTIFIER, platform: "appletvos")
macos_build = latest_testflight_build_number(api_key: api_key, app_identifier: DEVELOPER_APP_IDENTIFIER, platform: "osx")
max_build = [ios_build, tvos_build, macos_build].max
# Write to repo root (Fastfile is in fastlane/ subdir)
output_path = File.expand_path("../latest_build_number.txt", __dir__)
File.write(output_path, max_build.to_s)
UI.success("Latest TestFlight build number: #{max_build}")
max_build
end
desc "Bump build number and commit"
lane :bump_build do
increment_build_number(xcodeproj: XCODEPROJ)
commit_version_bump(
message: "Bump build number to #{get_build_number(xcodeproj: XCODEPROJ)}",
xcodeproj: XCODEPROJ
)
end
desc "Bump version number and commit"
lane :bump_version do
increment_version_number(xcodeproj: XCODEPROJ)
commit_version_bump(
message: "Bump version number to #{get_version_number(xcodeproj: XCODEPROJ, target: SCHEME)}",
xcodeproj: XCODEPROJ
)
end
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
ensure_temp_keychain(TEMP_KEYCHAIN_USER, TEMP_KEYCHAIN_PASSWORD)
api_key = app_store_connect_api_key(
key_id: DEVELOPER_KEY_ID,
issuer_id: DEVELOPER_KEY_ISSUER_ID,
key_content: DEVELOPER_KEY_CONTENT,
is_key_content_base64: true
)
build = get_build_number(xcodeproj: XCODEPROJ)
version = get_version_number(
xcodeproj: XCODEPROJ,
target: SCHEME
)
match(
type: 'appstore',
platform: 'ios',
app_identifier: ["#{DEVELOPER_APP_IDENTIFIER}", "#{DEVELOPER_APP_IDENTIFIER}.ShareExtension"],
git_basic_authorization: Base64.strict_encode64(GIT_AUTHORIZATION),
readonly: true,
keychain_name: TEMP_KEYCHAIN_USER,
keychain_password: TEMP_KEYCHAIN_PASSWORD,
api_key: api_key
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{DEVELOPER_APP_IDENTIFIER}",
targets: [SCHEME]
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{DEVELOPER_APP_IDENTIFIER}.ShareExtension",
targets: ["YatteeShareExtension"]
)
build_app(
scheme: SCHEME,
destination: "generic/platform=iOS",
output_directory: "fastlane/builds/#{version}-#{build}/iOS",
output_name: "#{APP_NAME}-#{version}-iOS.ipa",
export_options: {
provisioningProfiles: {
"#{DEVELOPER_APP_IDENTIFIER}" => "match AppStore #{DEVELOPER_APP_IDENTIFIER}",
"#{DEVELOPER_APP_IDENTIFIER}.ShareExtension" => "match AppStore #{DEVELOPER_APP_IDENTIFIER}.ShareExtension"
}
}
)
changelog_path = File.expand_path('../CHANGELOG.md', __dir__)
changelog = File.exist?(changelog_path) ? File.read(changelog_path) : ""
upload_to_testflight(
api_key: api_key,
ipa: lane_context[SharedValues::IPA_OUTPUT_PATH],
changelog: changelog
)
end
end
platform :tvos do
desc "Push a new beta build to TestFlight"
lane :beta do
ensure_temp_keychain(TEMP_KEYCHAIN_USER, TEMP_KEYCHAIN_PASSWORD)
api_key = app_store_connect_api_key(
key_id: DEVELOPER_KEY_ID,
issuer_id: DEVELOPER_KEY_ISSUER_ID,
key_content: DEVELOPER_KEY_CONTENT,
is_key_content_base64: true
)
build = get_build_number(xcodeproj: XCODEPROJ)
version = get_version_number(
xcodeproj: XCODEPROJ,
target: SCHEME
)
match(
type: 'appstore',
platform: 'tvos',
app_identifier: ["#{DEVELOPER_APP_IDENTIFIER}", "#{DEVELOPER_APP_IDENTIFIER}.TopShelf"],
git_basic_authorization: Base64.strict_encode64(GIT_AUTHORIZATION),
readonly: true,
keychain_name: TEMP_KEYCHAIN_USER,
keychain_password: TEMP_KEYCHAIN_PASSWORD,
api_key: api_key
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{DEVELOPER_APP_IDENTIFIER} tvos",
targets: [SCHEME]
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{DEVELOPER_APP_IDENTIFIER}.TopShelf tvos",
targets: ["YatteeTopShelf"]
)
build_app(
scheme: SCHEME,
destination: "generic/platform=tvOS",
output_directory: "fastlane/builds/#{version}-#{build}/tvOS",
output_name: "#{APP_NAME}-#{version}-tvOS.ipa",
export_method: "app-store",
export_options: {
provisioningProfiles: {
"#{DEVELOPER_APP_IDENTIFIER}" => "match AppStore #{DEVELOPER_APP_IDENTIFIER} tvos",
"#{DEVELOPER_APP_IDENTIFIER}.TopShelf" => "match AppStore #{DEVELOPER_APP_IDENTIFIER}.TopShelf tvos"
}
}
)
changelog_path = File.expand_path('../CHANGELOG.md', __dir__)
changelog = File.exist?(changelog_path) ? File.read(changelog_path) : ""
upload_to_testflight(
api_key: api_key,
ipa: lane_context[SharedValues::IPA_OUTPUT_PATH],
changelog: changelog
)
end
end
platform :mac do
desc "Push a new beta build to TestFlight"
lane :beta do
ensure_temp_keychain(TEMP_KEYCHAIN_USER, TEMP_KEYCHAIN_PASSWORD)
api_key = app_store_connect_api_key(
key_id: DEVELOPER_KEY_ID,
issuer_id: DEVELOPER_KEY_ISSUER_ID,
key_content: DEVELOPER_KEY_CONTENT,
is_key_content_base64: true
)
build = get_build_number(xcodeproj: XCODEPROJ)
version = get_version_number(
xcodeproj: XCODEPROJ,
target: SCHEME
)
match(
type: 'appstore',
platform: 'macos',
additional_cert_types: ['mac_installer_distribution'],
app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
git_basic_authorization: Base64.strict_encode64(GIT_AUTHORIZATION),
readonly: true,
keychain_name: TEMP_KEYCHAIN_USER,
keychain_password: TEMP_KEYCHAIN_PASSWORD,
api_key: api_key
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{DEVELOPER_APP_IDENTIFIER} macos",
targets: [SCHEME]
)
build_mac_app(
scheme: SCHEME,
sdk: "macosx",
destination: "generic/platform=macOS",
output_directory: "fastlane/builds/#{version}-#{build}/macOS",
output_name: "#{APP_NAME}-#{version}-macOS",
export_method: "app-store",
export_options: {
provisioningProfiles: {
"#{DEVELOPER_APP_IDENTIFIER}" => "match AppStore #{DEVELOPER_APP_IDENTIFIER} macos"
}
}
)
changelog_path = File.expand_path('../CHANGELOG.md', __dir__)
changelog = File.exist?(changelog_path) ? File.read(changelog_path) : ""
upload_to_testflight(
api_key: api_key,
pkg: lane_context[SharedValues::PKG_OUTPUT_PATH],
changelog: changelog
)
end
desc "Build for Developer ID distribution and notarize"
lane :build_and_notarize do
ensure_temp_keychain(TEMP_KEYCHAIN_USER, TEMP_KEYCHAIN_PASSWORD)
api_key = app_store_connect_api_key(
key_id: DEVELOPER_KEY_ID,
issuer_id: DEVELOPER_KEY_ISSUER_ID,
key_content: DEVELOPER_KEY_CONTENT,
is_key_content_base64: true
)
build = get_build_number(xcodeproj: XCODEPROJ)
version = get_version_number(
xcodeproj: XCODEPROJ,
target: SCHEME
)
match(
type: 'developer_id',
platform: 'macos',
app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
git_basic_authorization: Base64.strict_encode64(GIT_AUTHORIZATION),
readonly: true,
keychain_name: TEMP_KEYCHAIN_USER,
keychain_password: TEMP_KEYCHAIN_PASSWORD,
api_key: api_key
)
update_code_signing_settings(
use_automatic_signing: false,
path: XCODEPROJ,
team_id: TEAM_ID,
code_sign_identity: "Developer ID Application",
profile_name: "match Direct #{DEVELOPER_APP_IDENTIFIER} macos",
targets: [SCHEME],
build_configurations: ["Release-DeveloperID"]
)
build_mac_app(
scheme: SCHEME,
sdk: "macosx",
destination: "generic/platform=macOS",
configuration: "Release-DeveloperID",
output_directory: "fastlane/builds/#{version}-#{build}/macOS",
output_name: APP_NAME,
export_method: "developer-id",
export_options: {
provisioningProfiles: {
"#{DEVELOPER_APP_IDENTIFIER}" => "match Direct #{DEVELOPER_APP_IDENTIFIER} macos"
}
}
)
notarize(
package: "fastlane/builds/#{version}-#{build}/macOS/#{APP_NAME}.app",
bundle_id: "#{DEVELOPER_APP_IDENTIFIER}",
api_key: api_key,
print_log: true
)
# Staple the notarization ticket so Gatekeeper doesn't need to phone home
# when the user first launches from Sparkle/DMG.
sh "xcrun stapler staple 'fastlane/builds/#{version}-#{build}/macOS/#{APP_NAME}.app'"
# Package the stapled .app into a .zip (Sparkle) and .dmg (Homebrew / direct).
dir = "fastlane/builds/#{version}-#{build}/macOS"
app = "#{dir}/#{APP_NAME}.app"
zip = "#{dir}/#{APP_NAME}-#{version}-macOS.zip"
dmg = "#{dir}/#{APP_NAME}-#{version}-macOS.dmg"
# .zip: `ditto -c -k --keepParent` is Sparkle's expected archive layout
# (preserves the .app wrapper, resource forks, symlinks).
sh "/usr/bin/ditto -c -k --keepParent '#{app}' '#{zip}'"
# .dmg: read-only UDZO, overwrites any stale image from previous runs.
sh "/bin/rm -f '#{dmg}'"
sh "/usr/bin/hdiutil create -volname '#{APP_NAME} #{version}' -srcfolder '#{app}' -ov -format UDZO '#{dmg}'"
end
end