b0VIM 8.09,\g_joshJoshs-MacBook-Pro.local~josh/Projects/fastlane/fastlane/pilot/lib/pilot/build_manager.rbutf-8 3210#"! UtpSQTYVPTad{Sn; _^   V J @ ? L ? >  ] S R   (JkskjB-#{z # 2. App info # 1. Demo account required # Update beta app meta info end UI.user_error!("No build to distribute!") if build.nil? build ||= Spaceship::TestFlight::Build.latest(app_id: app.apple_id, platform: fetch_app_platform) end config[:app_identifier] = UI.input("App Identifier: ") if config[:apple_id].to_s.length == 0 && config[:app_identifier].to_s.length == 0 start(options) def distribute(options, build: nil) end return latest_build end UI.important("Uploaded app #{app_version} - #{app_build}, but received build #{latest_build.train_version} - #{latest_build.build_version}. If you want to wait for uploaded build to be finished processing, use the `wait_for_uploaded_build` option") unless latest_build.train_version == app_version && latest_build.build_version == app_build latest_build = FastlaneCore::BuildWatcher.wait_for_build_processing_to_be_complete(app_id: app.apple_id, platform: platform, train_version: app_version, build_version: app_build, poll_interval: config[:wait_processing_interval], strict_build_watch: config[:wait_for_uploaded_build]) app_build = FastlaneCore::IpaFileAnalyser.fetch_app_build(config[:ipa]) app_version = FastlaneCore::IpaFileAnalyser.fetch_app_version(config[:ipa]) platform = fetch_app_platform def wait_for_build_processing_to_be_complete end distribute(options, build: latest_build) latest_build = wait_for_build_processing_to_be_complete UI.message("If you want to skip waiting for the processing to be finished, use the `skip_waiting_for_build_processing` option") end return UI.important("This means that no changelog will be set and no build will be distributed to testers") UI.important("Skip waiting for build processing") if config[:skip_waiting_for_build_processing] UI.success("Successfully uploaded the new binary to App Store Connect") end UI.user_error!("Error uploading ipa file, for more information see above") unless result result = transporter.upload(app.apple_id, package_path) transporter = transporter_for_selected_team(options) platform: platform) package_path: dir, ipa_path: config[:ipa], package_path = FastlaneCore::IpaUploadPackageBuilder.new.generate(app_id: app.apple_id, platform = fetch_app_platform dir = Dir.mktmpdir UI.success("Ready to upload new build to TestFlight (App: #{app.apple_id})...") end end UI.user_error!("No changelog provided for new build. Please either disable `distribute_external` or provide a changelog using the `changelog` option") else options[:changelog] = UI.input("No changelog provided for new build. You can provide a changelog using the `changelog` option. For now, please provide a changelog here:") if UI.interactive? if options[:changelog].nil? && options[:distribute_external] == true UI.user_error!("No ipa file given") unless config[:ipa] options[:changelog] = self.class.sanitize_changelog(options[:changelog]) if options[:changelog] start(options) def upload(options) class BuildManager < Managermodule Pilotrequire_relative 'manager'require 'fastlane_core/ipa_upload_package_builder'require 'fastlane_core/build_watcher'require 'fastlane_core/itunes_transporter'require 'emoji_regex'require 'terminal-table'require 'tmpdir'ad=P?n m b 3  z y c = 2 X  y S R    kjBt^bW([Z)  }end end end client.patch_build_beta_details(build_beta_details_id: build_beta_details_id, attributes: attributes) attribeeeeeeend end endend end end client.patch_build_beta_details(build_beta_details_id: build_beta_details_id, attributes: attributes) attributes[:autoNotifyEnabled] = info[:auto_notify_enabled] if info.key?(:auto_notify_enabled) attributes = {} build_beta_details_id = resp.first["id"] resp = client.get_build_beta_details(filter: { build: build_id }) build_id = build["id"] build = build.find_app_store_connect_build client = Spaceship::ConnectAPI::Base.client def update_build_beta_details(build, info) end end client.post_beta_build_localizations(build_id: build_id, attributes: attributes) attributes[:locale] = locale if locale else client.patch_beta_build_localizations(localization_id: localization_id, attributes: attributes) if localization_id client = Spaceship::ConnectAPI::Base.client attributes[:whatsNew] = info[:whats_new] if info.key?(:whats_new) attributes = {} def update_localized_build_review_for_lang(build_id, localization_id, locale, info) end end update_localized_build_review_for_lang(build_id, localization_id, lang_code, info) if info info = default_info unless info info = info_by_lang[lang_code] langs_localization_ids.each do |lang_code, localization_id| # Create or update localized app review info end langs_localization_ids[locale.to_sym] = localization_id locale = attributes["locale"] attributes = localization["attributes"] localization_id = localization["id"] localizations.each do |localization| localizations = client.get_beta_build_localizations(filter: { build: build_id }) client = Spaceship::ConnectAPI::Base.client # Validate locales exist langs_localization_ids = {} # Initialize hash of lang codes end default_info = info_by_lang.delete(:default) else info_by_lang.delete(:default) if default_info info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h build_id = resp.first["id"] resp = Spaceship::ConnectAPI::Base.client.get_builds(filter: { expired: false, processingState: "PROCESSING,VALID", version: build.build_version }) def update_localized_build_review(build, info_by_lang, default_info: nil) end end client.post_beta_app_localizations(app_id: app_id, attributes: attributes) attributes[:locale] = locale if locale else client.patch_beta_app_localizations(localization_id: localization_id, attributes: attributes) if localization_id client = Spaceship::ConnectAPI::Base.client attributes[:description] = info[:description] if info.key?(:description) attributes[:tvOsPrivacyPolicy] = info[:tv_os_privacy_policy_url] if info.key?(:tv_os_privacy_policy_url) attributes[:privacyPolicyUrl] = info[:privacy_policy_url] if info.key?(:privacy_policy_url) attributes[:marketingUrl] = info[:marketing_url] if info.key?(:marketing_url) attributes[:feedbackEmail] = info[:feedback_email] if info.key?(:feedback_email) attributes = {}adyQ|QP'c6 E  ~ W  ; ^ B  u 8 |P>vGQA80/yx original_length = changelog.length if changelog && changelog.length > max_changelog_length max_changelog_length = 4000 def self.truncate_changelog(changelog) end }) auto_notify_enabled: options[:notify_external_testers] update_build_beta_details(build, { end end UI.user_error!("Could not set changelog: #{ex}") rescue => ex UI.success("Successfully set the changelog for build") update_localized_build_review(build, {}, default_info: { whats_new: options[:changelog] }) begin elsif should_update_build_information?(options) update_localized_build_review(build, options[:localized_build_info]) if should_update_localized_build_information?(options) end end UI.user_error!("Could not set beta_app_feedback_email and/or beta_app_description: #{ex}") rescue => ex UI.success("Successfully set the beta_app_feedback_email and/or beta_app_description") update_localized_app_review(build.app_id, {}, default_info: default_info) begin default_info[:description] = options[:beta_app_description] if options[:beta_app_description] default_info[:feedback_email] = options[:beta_app_feedback_email] if options[:beta_app_feedback_email] default_info = {} elsif should_update_app_test_information?(options) update_localized_app_review(build.app_id, options[:localized_app_info]) if should_update_localized_app_information?(options) end update_review_detail(build.app_id, options[:beta_app_review_info]) if should_update_beta_app_review_info(options) update_review_detail(build.app_id, { demo_account_required: options[:demo_account_required] }) # Setting account required wth AppStore Connect API def update_beta_app_meta(options, build) end )) rows: FastlaneCore::PrintTable.transform_output(rows) headings: ["Version #", "Build #", "Installs"], title: "#{app.name} Builds".green, puts(Terminal::Table.new( rows = builds.collect { |build| describe_build(build) } builds.sort! { |a, b| a.upload_date <=> b.upload_date } # sort by upload_date builds = app.all_processing_builds(platform: platform) + app.builds(platform: platform) platform = fetch_app_platform(required: false) end config[:app_identifier] = UI.input("App Identifier: ") if config[:apple_id].to_s.length == 0 && config[:app_identifier].to_s.length == 0 start(options) def list(options) end UI.success("Successfully distributed build to #{type} testers 🚀") type = options[:distribute_external] ? 'External' : 'Internal' distribute_build(build, options) end end UI.success("Expired previous build: #{waiting_for_review_build.train_version} - #{waiting_for_review_build.build_version}") waiting_for_review_build.expire! UI.important("Expiring build: #{waiting_for_review_build.train_version} - #{waiting_for_review_build.build_version}") UI.important("Another build is already in review. Going to expire that build and submit the new one.") unless waiting_for_review_build.nil? waiting_for_review_build = Spaceship::TestFlight::Build.all_waiting_for_review(app_id: build.app_id, platform: fetch_app_platform).first if options[:reject_build_waiting_for_review] return if config[:skip_submission] update_beta_app_meta(options, build) # 5. Auto notify enabled with config[:notify_external_testers] # 4. Localized build info # 3. Localized app infoadY{S$ f =       ~ m e d 0  b G    E  /."g*~kV{<;;s if options[:groups] end end raise ex unless updated_build.approved? updated_build = Spaceship::TestFlight::Build.find(app_id: uploaded_build.app_id, build_id: uploaded_build.id) UI.message("Submitting the build for review timed out, trying to recover.") raise ex unless ex.to_s.include?("504") # the approved state, this is a temporary workaround. # App Store Connect currently may 504 on this request even though it manages to get the build in rescue => ex uploaded_build.submit_for_testflight_review! begin if options[:groups] || options[:distribute_external] set_export_compliance_if_needed(uploaded_build, options) # This is where we could add a check to see if encryption is required and has been updated UI.message("Distributing new build to testers: #{uploaded_build.train_version} - #{uploaded_build.build_version}") def distribute_build(uploaded_build, options) end end return generic_transporter UI.verbose("Couldn't infer a provider short name for team with id #{Spaceship::Tunes.client.team_id} automatically: #{ex}. Proceeding without provider short name.") rescue => ex return FastlaneCore::ItunesTransporter.new(options[:username], nil, false, provider_id) UI.verbose("Inferred provider id #{provider_id} for team #{name}.") provider_id = generic_transporter.provider_ids[name] name = team['contentProvider']['name'] team = Spaceship::Tunes.client.teams.find { |t| t['contentProvider']['contentProviderId'].to_s == Spaceship::Tunes.client.team_id } begin return generic_transporter unless options[:itc_provider].nil? && Spaceship::Tunes.client.teams.count > 1 generic_transporter = FastlaneCore::ItunesTransporter.new(options[:username], nil, false, options[:itc_provider]) def transporter_for_selected_team(options) # If there are fewer than two teams, don't infer the provider. # If there are multiple teams, infer the provider from the selected team name. # If itc_provider was explicitly specified, use it. end !options[:localized_build_info].nil? def should_update_localized_build_information?(options) end !options[:localized_app_info].nil? def should_update_localized_app_information?(options) end options[:beta_app_description].to_s.length > 0 || options[:beta_app_feedback_email].to_s.length > 0 def should_update_app_test_information?(options) end options[:changelog].to_s.length > 0 def should_update_build_information?(options) end !options[:beta_app_review_info].nil? def should_update_beta_app_review_info(options) end return row build.install_count] build.build_version, row = [build.train_version, def describe_build(build) private end truncate_changelog(changelog) changelog = strip_emoji(changelog) def self.sanitize_changelog(changelog) end changelog end UI.important("Emoji symbols have been removed from the changelog, since they're not allowed by Apple.") changelog.gsub!(EmojiRegex::Regex, "") if changelog && changelog =~ EmojiRegex::Regex def self.strip_emoji(changelog) end changelog end UI.important("Changelog has been truncated since it exceeds Apple's #{max_changelog_length} character limit. It currently contains #{original_length} characters.") changelog = "#{changelog[0...max_changelog_length - bottom_message.length]}#{bottom_message}" bottom_message = "..."ad Vj^?j p d Z Y N F E  ]  F E 4  v " a Ch xwa;0V}WV  onF def update_localized_app_review_for_lang(app_id, localization_id, locale, info) end end update_localized_app_review_for_lang(app_id, localization_id, lang_code, info) if info info = default_info unless info info = info_by_lang[lang_code] langs_localization_ids.each do |lang_code, localization_id| # Create or update localized app review info end langs_localization_ids[locale.to_sym] = localization_id locale = attributes["locale"] attributes = localization["attributes"] localization_id = localization["id"] localizations.each do |localization| localizations = client.get_beta_app_localizations(filter: { app: app_id }) client = Spaceship::ConnectAPI::Base.client # Validate locales exist langs_localization_ids = {} # Initialize hash of lang codes end default_info = info_by_lang.delete(:default) else info_by_lang.delete(:default) if default_info info_by_lang = info_by_lang.collect { |k, v| [k.to_sym, v] }.to_h def update_localized_app_review(app_id, info_by_lang, default_info: nil) end client.patch_beta_app_review_detail(app_id: app_id, attributes: attributes) client = Spaceship::ConnectAPI::Base.client attributes[:notes] = info[:notes] if info.key?(:notes) attributes[:demoAccountRequired] = info[:demo_account_required] if info.key?(:demo_account_required) attributes[:demoAccountPassword] = info[:demo_account_password] if info.key?(:demo_account_password) attributes[:demoAccountName] = info[:demo_account_name] if info.key?(:demo_account_name) attributes[:contactPhone] = info[:contact_phone] if info.key?(:contact_phone) attributes[:contactLastName] = info[:contact_last_name] if info.key?(:contact_last_name) attributes[:contactFirstName] = info[:contact_first_name] if info.key?(:contact_first_name) attributes[:contactEmail] = info[:contact_email] if info.key?(:contact_email) attributes = {} info = info.collect { |k, v| [k.to_sym, v] }.to_h def update_review_detail(app_id, info) end end wait_for_build_processing_to_be_complete UI.important("Set 'ITSAppUsesNonExemptEncryption' in the 'Info.plist' to skip this step and speed up the submission") UI.important("Export comlpiance has been set to '#{uses_non_exempt_encryption}'. Need to wait for build to finishing processing again...") client.patch_builds(build_id: build["id"], attributes: attributes) client = Spaceship::ConnectAPI::Base.client attributes = { usesNonExemptEncryption: uses_non_exempt_encryption } uses_non_exempt_encryption = options[:uses_non_exempt_encryption] if build_attributes["usesNonExemptEncryption"].nil? build_attributes = build["attributes"] || {} build = uploaded_build.find_app_store_connect_build def set_export_compliance_if_needed(uploaded_build, options) end true end end UI.user_error!("You must specify at least one group using the `:groups` option to distribute externally") if external_group.nil? && options[:groups].nil? uploaded_build.add_group!(external_group) unless external_group.nil? external_group = Spaceship::TestFlight::Group.default_external_group(app_id: uploaded_build.app_id) if options[:distribute_external] end end uploaded_build.add_group!(group) groups.each do |group| end options[:groups].include?(group.name) groups = Spaceship::TestFlight::Group.filter_groups(app_id: uploaded_build.app_id) do |group|