def app_identifiers ["io.bluewallet.bluewallet", "io.bluewallet.bluewallet.watch", "io.bluewallet.bluewallet.watch.extension", "io.bluewallet.bluewallet.Stickers", "io.bluewallet.bluewallet.MarketWidget"] end platform :ios do before_all do |lane, options| UI.message("Setting up for all lanes...") UI.message("Discarding all untracked changes before running any lane...") sh("git clean -fd") sh("git checkout -- .") end desc "Register new devices from a file" lane :register_devices_from_txt do UI.message("Registering new devices from file...") csv_path = "../../devices.txt" # Update this with the actual path to your file # Registering devices using the devices_file parameter register_devices( devices_file: csv_path ) UI.message("Devices registered successfully.") app_identifiers.each do |app_identifier| match( type: "development", app_identifier: app_identifier, readonly: false, # This will regenerate the provisioning profile if needed force_for_new_devices: true # This forces match to add new devices to the profile ) end UI.message("Development provisioning profiles updated.") end desc "Create a temporary keychain" lane :create_temp_keychain do UI.message("Creating a temporary keychain...") create_keychain( name: "temp_keychain", password: ENV["KEYCHAIN_PASSWORD"], default_keychain: true, unlock: true, timeout: 3600, lock_when_sleeps: true ) UI.message("Temporary keychain created successfully.") end desc "Synchronize certificates and provisioning profiles" lane :setup_provisioning_profiles do |options| UI.message("Setting up provisioning profiles...") target_to_app_identifier = { 'BlueWallet' => 'io.bluewallet.bluewallet', 'BlueWalletWatch' => 'io.bluewallet.bluewallet.watch', 'BlueWalletWatchExtension' => 'io.bluewallet.bluewallet.watch.extension', 'Stickers' => 'io.bluewallet.bluewallet.Stickers', 'MarketWidget' => 'io.bluewallet.bluewallet.MarketWidget' } platform = options[:platform] || "ios" # Default to iOS if not specified target_to_app_identifier.each do |target, app_identifier| match( git_basic_authorization: ENV["MATCH_GIT_BASIC_AUTHORIZATION"], git_url: ENV["GIT_URL"], type: "appstore", platform: platform, app_identifier: app_identifier, team_id: ENV["ITC_TEAM_ID"], team_name: ENV["ITC_TEAM_NAME"], readonly: true, keychain_name: "temp_keychain", keychain_password: ENV["KEYCHAIN_PASSWORD"] ) end end desc "Fetch development certificates and provisioning profiles for Mac Catalyst" lane :fetch_dev_profiles_catalyst do match( type: "development", platform: "catalyst", app_identifier: app_identifiers, readonly: true ) end desc "Fetch App Store certificates and provisioning profiles for Mac Catalyst" lane :fetch_appstore_profiles_catalyst do match( type: "appstore", platform: "catalyst", app_identifier: app_identifiers, readonly: true ) end desc "Clear derived data" lane :clear_derived_data_lane do UI.message("Clearing derived data...") clear_derived_data end desc "Increment build number" lane :increment_build_number_lane do UI.message("Incrementing build number to current timestamp...") # Set the new build number increment_build_number( xcodeproj: "BlueWallet.xcodeproj", build_number: ENV["NEW_BUILD_NUMBER"] ) UI.message("Build number set to: #{ENV['NEW_BUILD_NUMBER']}") end desc "Install CocoaPods dependencies" lane :install_pods do UI.message("Installing CocoaPods dependencies...") cocoapods end desc "Build the application" lane :build_app_lane do UI.message("Building the application...") build_app( scheme: "BlueWallet", workspace: "BlueWallet.xcworkspace", export_method: "app-store", include_bitcode: true, configuration: "Release", skip_profile_detection: true, include_symbols: true, export_team_id: ENV["ITC_TEAM_ID"], export_options: { signingStyle: "manual", provisioningProfiles: { 'io.bluewallet.bluewallet' => 'match AppStore io.bluewallet.bluewallet', 'io.bluewallet.bluewallet.watch' => 'match AppStore io.bluewallet.bluewallet.watch', 'io.bluewallet.bluewallet.watch.extension' => 'match AppStore io.bluewallet.bluewallet.watch.extension', 'io.bluewallet.bluewallet.Stickers' => 'match AppStore io.bluewallet.bluewallet.Stickers', 'io.bluewallet.bluewallet.MarketWidget' => 'match AppStore io.bluewallet.bluewallet.MarketWidget' } }, xcargs: "GCC_PREPROCESSOR_DEFINITIONS='$(inherited) VERBOSE_LOGGING=1'", output_directory: "./build", # Directory where the IPA file will be stored output_name: "BlueWallet.#{ENV['PROJECT_VERSION']}(#{ENV['NEW_BUILD_NUMBER']}).ipa", buildlog_path: "./build_logs" ) end desc "Upload to TestFlight without Processing Wait" lane :upload_to_testflight_lane do attempts = 0 max_attempts = 3 begin UI.message("Uploading to TestFlight without processing wait...") changelog = ENV["LATEST_COMMIT_MESSAGE"] upload_to_testflight( api_key_path: "appstore_api_key.json", ipa: "./build/BlueWallet.#{ENV['PROJECT_VERSION']}(#{ENV['NEW_BUILD_NUMBER']}).ipa", skip_waiting_for_build_processing: true, # Do not wait for processing changelog: changelog ) rescue => exception attempts += 1 if attempts <= max_attempts wait_time = 180 # 3 minutes in seconds UI.message("Attempt ##{attempts} failed with error: #{exception.message}. Waiting #{wait_time} seconds before trying again...") sleep(wait_time) retry else UI.error("Failed after #{max_attempts} attempts. Error: #{exception.message}") raise exception end end end desc "Deploy to TestFlight" lane :deploy do |options| UI.message("Starting build process...") # Update the WWDR certificate update_wwdr_certificate setup_app_store_connect_api_key setup_provisioning_profiles clear_derived_data_lane increment_build_number_lane unless File.directory?("Pods") install_pods end build_app_lane upload_to_testflight_lane # Clean up and delete the temporary keychain delete_keychain(name: "temp_keychain") # Mark deployment as completed for the current commit last_commit = last_git_commit already_built_flag = ".already_built_#{last_commit[:sha]}" File.write(already_built_flag, Time.now.to_s) end desc "Build and distribute the Mac Catalyst app for BlueWallet-NoLDK (Universal)" lane :build_and_distribute_catalyst_universal do |options| # Conditionally fetch the right provisioning profiles if options[:type] == "development" fetch_dev_profiles_catalyst else fetch_appstore_profiles_catalyst end # Build the Mac Catalyst app (Universal Build for Apple Silicon and Intel) build_app( scheme: "BlueWallet-NoLDK", configuration: "Release", catalyst_platform: "macos", export_method: options[:type] || "app-store", # Default to app-store if not specified xcargs: "ARCHS='arm64 x86_64' VALID_ARCHS='arm64 x86_64'", export_options: { provisioningProfiles: { "io.bluewallet.bluewallet" => "Provisioning Profile Name for Catalyst", # Include other identifiers as necessary }, signingStyle: "manual", } ) # Optionally, upload the build to TestFlight or App Store Connect for app-store type if options[:type] != "development" upload_to_testflight(skip_waiting_for_build_processing: true) end end desc "Update 'What's New' section in App Store Connect for all localizations" lane :update_release_notes do # Path to the release notes file # Make sure to edit it to remove any mention of non-Apple products release_notes_path = "../../release-notes.txt" # Ensure the release notes file exists unless File.exist?(release_notes_path) UI.error("Release notes file does not exist at path: #{release_notes_path}") next # Skip the rest of the lane if file not found end # Read release notes from file release_notes_text = File.read(release_notes_path) # Log the content of release notes UI.message("Release Notes Content:\n#{release_notes_text}") # Define version number app_version = get_version_number # Make sure this gets the correct version number UI.message("Version being updated: #{app_version}") # Hash mapping language codes to release notes text localized_release_notes = { 'en-US' => release_notes_text, # English (U.S.) - Primary 'ar-SA' => release_notes_text, # Arabic 'zh-Hans' => release_notes_text, # Chinese (Simplified) 'da' => release_notes_text, # Danish 'nl-NL' => release_notes_text, # Dutch 'fi' => release_notes_text, # Finnish 'fr-FR' => release_notes_text, # French 'de-DE' => release_notes_text, # German 'he' => release_notes_text, # Hebrew 'hu' => release_notes_text, # Hungarian 'it' => release_notes_text, # Italian 'pt-BR' => release_notes_text, # Portuguese (Brazil) 'pt-PT' => release_notes_text, # Portuguese (Portugal) 'ro' => release_notes_text, # Romanian 'ru' => release_notes_text, # Russian 'es-MX' => release_notes_text, # Spanish (Mexico) 'es-ES' => release_notes_text, # Spanish (Spain) 'sv' => release_notes_text, # Swedish } # Log which version and what content is being set localized_release_notes.each do |locale, notes| UI.message("Setting release notes for #{locale}:\n#{notes}\n") end # Update release notes in App Store Connect for all localizations deliver( app_identifier: app_identifiers.first, # Use the first app identifier app_version: app_version, # Use the fetched app version skip_metadata: true, # We are updating metadata skip_screenshots: true, skip_binary_upload: true, force: true, # Skip HTML report verification release_notes: localized_release_notes, submit_for_review: false, # Change this to true if you want to automatically submit the version for review automatic_release: false # Change to true if you want the version to be released automatically once approved ) end end