Files
eatvio_app/ios/fastlane/Fastfile
Anatoliy Poloz fc28062bb5 fix(tf-ci)
2026-11-06 13:16:36 +03:00

184 lines
6.7 KiB
Ruby

# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
UI.message "Current working directory: #{Dir.pwd}"
UI.message "Checking API key file..."
possible_paths = [
"../AuthKey.p8",
"./AuthKey.p8",
"../../AuthKey.p8",
File.expand_path("../AuthKey.p8"),
File.expand_path("./AuthKey.p8")
]
auth_key_path = nil
possible_paths.each do |path|
if File.exist?(path)
UI.message "Found AuthKey.p8 at: #{path}"
auth_key_path = File.expand_path(path)
break
end
end
if auth_key_path.nil?
UI.user_error!("API key file not found. Tried paths: #{possible_paths.join(', ')}")
end
UI.message "Using auth_key_path: #{auth_key_path}"
# Authenticate to App Store Connect early:
# needed here by latest_testflight_build_number, not only by upload_to_testflight.
app_store_connect_api_key(
key_id: "325JS9JCZH",
issuer_id: "dfa27d1d-2bb8-4d4d-847e-0b32f9b880b3",
key_filepath: auth_key_path,
duration: 1200,
in_house: false
)
# Configure Flutter for release mode
UI.message "Configuring Flutter for release mode..."
sh("cd .. && flutter build ios --config-only --release")
# Read version and build number from Flutter Generated.xcconfig.
# They are only a floor: the actual build number is derived from TestFlight below,
# so repeated CI runs on the same commit can never hit
# ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE ("bundle version must be higher").
generated_xcconfig_path = File.join("..", "Flutter", "Generated.xcconfig")
flutter_build_name = nil
flutter_build_number = nil
if File.exist?(generated_xcconfig_path)
File.foreach(generated_xcconfig_path) do |line|
if (m = line.match(/FLUTTER_BUILD_NAME=(.*)/))
flutter_build_name = m[1].strip
elsif (m = line.match(/FLUTTER_BUILD_NUMBER=(.*)/))
flutter_build_number = m[1].strip
end
end
end
if flutter_build_name.nil? || flutter_build_number.nil?
UI.user_error!("Could not find FLUTTER_BUILD_NAME/FLUTTER_BUILD_NUMBER in #{generated_xcconfig_path}")
end
UI.message "Flutter version from pubspec: #{flutter_build_name} (#{flutter_build_number})"
# Apple closes a version train once that version is approved/released.
# Uploading into a closed train wastes a full ~8 min build and fails only
# at the upload step with altool 90186 ("closed for new build submissions")
# / 90062 ("must be higher than the previously approved version").
# Fail fast here, before building.
closed_train_states = [
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::READY_FOR_DISTRIBUTION,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PROCESSING_FOR_DISTRIBUTION,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_DEVELOPER_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_APPLE_RELEASE
].join(",")
asc_app = Spaceship::ConnectAPI::App.find("com.eatvioapp.app")
closed_train = nil
if asc_app.nil?
UI.important("App com.eatvioapp.app not found in App Store Connect, skipping closed-train guard")
else
closed_train = asc_app.get_app_store_versions(
filter: {
appVersionState: closed_train_states,
platform: Spaceship::ConnectAPI::Platform::IOS
}
).find do |v|
(Gem::Version.new(v.version_string) >= Gem::Version.new(flutter_build_name))
rescue ArgumentError
false
end
end
if closed_train
UI.user_error!(
"Version train #{closed_train.version_string} is closed for new submissions " \
"(#{closed_train.app_version_state}), pubspec version is #{flutter_build_name}. " \
"Bump the version in pubspec.yaml above #{closed_train.version_string} and re-run."
)
end
asc_latest_build_number = latest_testflight_build_number(
app_identifier: "com.eatvioapp.app",
version: flutter_build_name,
initial_build_number: flutter_build_number.to_i
)
new_build_number = [flutter_build_number.to_i, asc_latest_build_number].max + 1
UI.message "Latest TestFlight build number: #{asc_latest_build_number} -> uploading build #{new_build_number}"
match(type: "appstore", readonly: true)
# Set build number explicitly instead of incrementing
increment_build_number(
xcodeproj: "Runner.xcodeproj",
build_number: new_build_number
)
update_code_signing_settings(
use_automatic_signing: false,
path: "Runner.xcodeproj",
team_id: "VL68C36HYS",
targets: ["Runner"],
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore com.eatvioapp.app 1781098260"
)
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store",
# codesigning_identity: "Apple Distribution",
export_team_id: "VL68C36HYS"
)
begin
upload_to_testflight(
skip_waiting_for_build_processing: true,
skip_submission: true,
distribute_external: false,
notify_external_testers: false,
changelog: "TestFlight build #{new_build_number}",
verbose: true
)
rescue => ex
# Xcode 26 altool prints a harmless "ERROR: ... CDUploaderStateInfoResumeFilePath"
# line even when the package was delivered fine (UPLOAD SUCCEEDED appears in the
# same output). fastlane < 2.240.0 treats every ERROR line as a failure
# (fastlane#30130), so the lane would die after a successful upload.
# Real Apple rejections (90186 closed train, 90062 version too low, ...) always
# contain UPLOAD FAILED and/or iris-code - those must still fail the lane.
if ex.message.include?("CDUploaderStateInfoResumeFilePath") &&
!ex.message.include?("UPLOAD FAILED") &&
!ex.message.include?("iris-code")
UI.important("altool delivered the package but reported a bogus resume-file ERROR " \
"(fastlane#30130). Treating upload as successful - " \
"verify the build appears in TestFlight.")
else
UI.error "Upload to TestFlight failed: #{ex.message}"
UI.error "Full error: #{ex.backtrace.join("\n")}"
raise ex
end
end
end
end