b0VIM 9.0e3joshholtzJoshs-MacBook-Air.local~joshholtz/Developer/fastlane/fastlane/supply/lib/supply/client.rbutf-8 3210#"! Utpe clh]p1zeadAeyxRD {* u < ! Z  q d J ' b  ^]5GonS ogfZYAs_ML=dL>3 UI.user_error!("Google Api Error: #{e.mess UI.user_error!("Google Api Error: #{e.message} - #{message}") else retry tries_left -= 1 UI.error("Google Api Error: #{e.message} - #{message} - Retrying...") if tries_left.positive? end message = e.body else message = error["error"] && error["error"]["message"] if error end nil rescue JSON.parse(e.body) error = begin rescue Google::Apis::Error => e yield if block_given? tries_left ||= (ENV["SUPPLY_UPLOAD_MAX_RETRIES"] || 0).to_i def call_google_api private end self.client = service end service.root_url = params[:root_url] params[:root_url] << '/' unless params[:root_url].end_with?('/') # Google's client expects the root_url string to end with "/". if params[:root_url] service.authorization = auth_client service = self.class::SERVICE.new Google::Apis::RequestOptions.default.retries = 5 Google::Apis::ClientOptions.default.send_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.open_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.read_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.application_version = Fastlane::VERSION Google::Apis::ClientOptions.default.application_name = "fastlane (supply client)" end Google::Apis.logger.level = Logger::DEBUG if FastlaneCore::Env.truthy?("DEBUG") auth_client.fetch_access_token! UI.verbose("Fetching a new access token from Google...") auth_client = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: service_account_json, scope: self.class::SCOPE) def initialize(service_account_json: nil, params: nil) # @param service_account_json: The raw service account Json data # Initializes the service and its auth_client using the specified information end service_account_json end service_account_json = StringIO.new(params[:json_key_data]) elsif params[:json_key_data] service_account_json = File.open(File.expand_path(params[:json_key])) if params[:json_key] end end UI.user_error!("Could not load Google authentication. Make sure it has been added as an environment variable in 'json_key' or 'json_key_data'") else params[:json_key] = json_key_path UI.user_error!("Could not find service account json file at path '#{json_key_path}'") unless File.exist?(json_key_path) json_key_path = File.expand_path(json_key_path) json_key_path = UI.input("The service account json file used to authenticate with Google: ") UI.important("To not be asked about this value, you can specify it using 'json_key'") if UI.interactive? unless params[:json_key] || params[:json_key_data] def self.service_account_authentication(params: nil) # Supply authentication file end return self.new(service_account_json: service_account_data, params: params) service_account_data = self.service_account_authentication(params: params) params ||= Supply.config def self.make_from_config(params: nil) attr_accessor :client # Connecting with Google SERVICE = nil SCOPE = nil class AbstractGoogleServiceClientmodule Supply# rubocop:disable Metrics/ClassLengthrequire 'net/http'AndroidPublisher = Google::Apis::AndroidpublisherV3require 'google/apis/androidpublisher_v3'require 'googleauth'adeN43xwN o e ] \ F @ i U ?  ~ * v u \ 7  YSHBiU?,"Y9~tlk_^B# rubocop:enable Metrics/ClassLengthend end end UI.user_error!("You need to have an active edit, make sure to call `begin_edit`") unless @current_edit def ensure_active_edit! private end end ) content_type: 'application/octet-stream' upload_source: obb_file_path, expansion_file_type, apk_version_code, current_edit.id, current_package_name, client.upload_edit_expansionfile( call_google_api do ensure_active_edit! def upload_obb(obb_file_path: nil, apk_version_code: nil, expansion_file_type: nil) end end ) image_id image_type, language, current_edit.id, current_package_name, client.delete_edit_image( call_google_api do ensure_active_edit! def clear_screenshot(image_type: nil, language: nil, image_id: nil) # # @param [String] image_id The id of the screenshot to remove (as per `ImageListing#id`) # @param [String] language localization code (i.e. BCP-47 language tag as in `pt-BR`) # @param [String] image_type (e.g. phoneScreenshots, sevenInchScreenshots, ...) # # Remove a specific screenshot of a given image_type and language from the store listing end end ) image_type language, current_edit.id, current_package_name, client.deleteall_edit_image( call_google_api do ensure_active_edit! def clear_screenshots(image_type: nil, language: nil) # # @param [String] language localization code (i.e. BCP-47 language tag as in `pt-BR`) # @param [String] image_type (e.g. phoneScreenshots, sevenInchScreenshots, ...) # # Remove all screenshots of a given image_type and language from the store listing end end ) content_type: 'image/*' upload_source: image_path, image_type, language, current_edit.id, current_package_name, client.upload_edit_image( call_google_api do ensure_active_edit! def upload_image(image_path: nil, image_type: nil, language: nil) # # @param [String] language localization code (i.e. BCP-47 language tag as in `pt-BR`) # @param [String] image_type (e.g. phoneScreenshots, sevenInchScreenshots, ...) # # Upload an image or screenshot of a specific type for a given language to your store listing end end ImageListing.new(row.id, row.sha1, row.sha256, full_url) full_url = "#{row.url}=s0" # '=s0' param ensures full image size is returned (https://github.com/fastlane/fastlane/pull/14322#issuecomment-473012462) (result.images || []).map do |row| end ) image_type language, current_edit.id, current_package_name, client.list_edit_images( result = call_google_api do ensure_active_edit! def fetch_images(image_type: nil, language: nil) # # @return [Array] A list of ImageListing instances describing each image with its id, sha256 and urlad]iX>=won  q V C 9 8  y k a Y X t s ? 7 6 P O    = <  P~} D:21* end latest_version = tracks.select { |t| t.track == track }.map(&:releases).flatten.reject { |r| r.name.nil? }.max_by(&:name) else UI.user_error!(%(Unable to find latest version information from "#{Supply::Tracks::DEFAULT}" track. Please specify track information by using the '--track' option.)) if latest_version.nil? && track == Supply::Tracks::DEFAULT # Check if user specified '--track' option if version information from 'production' track is nil latest_version = tracks.select { |t| t.track == Supply::Tracks::DEFAULT }.map(&:releases).flatten.reject { |r| r.name.nil? }.max_by(&:name) def latest_version(track) end end Supply::ReleaseListing.new(filtered_track, filtered_release.name, filtered_release.version_codes, row.language, row.text) return filtered_release.release_notes.map do |row| end return [] UI.message("Version '#{version}' for '#{current_package_name}' does not seem to have any release notes. Nothing to download.") if filtered_release.release_notes.nil? # Since we can release on Internal/Alpha/Beta without release notes. filtered_release = filtered_track.releases.first { |r| !r.name.nil? && r.name == version } end UI.message("Found '#{version}' in '#{filtered_track.track}' track.") else return nil UI.user_error!("Unable to find version '#{version}' for '#{current_package_name}' in all tracks. Please double check the version number.") if filtered_track.nil? filtered_track = filtered_tracks.first end filtered_tracks = filtered_tracks.sort_by { |t| Supply::Tracks::DEFAULTS.index(t.track) || Float::INFINITY } # E.g.: A release might've been promoted from Alpha/Beta track. This means the release will be present in two or more tracks # Prefer tracks in production, beta, alpha, internal order if filtered_tracks.length > 1 filtered_tracks = tracks.select { |t| !t.releases.nil? && t.releases.any? { |r| r.name == version } } # Verify that tracks have releases ensure_active_edit! def release_listings(version) end return Array(result.bundles).map(&:version_code) result = call_google_api { client.list_edit_bundles(current_package_name, current_edit.id) } ensure_active_edit! def aab_version_codes # Get a list of all AAB version codes - returns the list of version codes end return Array(result.apks).map(&:version_code) result = call_google_api { client.list_edit_apks(current_package_name, current_edit.id) } ensure_active_edit! def apks_version_codes # Get a list of all APK version codes - returns the list of version codes end end raise return Listing.new(self, language) if e.status_code == 404 # create a new empty listing rescue Google::Apis::ClientError => e return Listing.new(self, language, result) ) language current_edit.id, current_package_name, result = client.get_edit_listing( begin ensure_active_edit! def listing_for_language(language) # Returns the listing for the given language filled with the current values if it already exists end end Listing.new(self, row.language, row) return result.listings.map do |row| result = call_google_api { client.list_edit_listings(current_package_name, current_edit.id) } ensure_active_edit! def listings # make sure to have an active edit # Get a list of all languages - returns the list #####################################################ad!pwqN&|8 1 '   ! j R , x n f ^ ] #   i;&ukcbB('~tjiA98q<$K10tXfe< current_package_name, client.upload_edit_bundle( result_upload = call_google_api do ensure_active_edit! def upload_bundle(path_to_aab) end end ) content_type: "application/octet-stream" upload_source: path_to_mapping, extension == ".zip" ? "nativeCode" : "proguard", apk_version_code, current_edit.id, current_package_name, client.upload_edit_deobfuscationfile( call_google_api do extension = File.extname(path_to_mapping).downcase ensure_active_edit! def upload_mapping(path_to_mapping, apk_version_code) end return result_upload.download_url end ) content_type: "application/octet-stream" upload_source: path_to_apk, package_name, client.uploadapk_internalappsharingartifact( result_upload = call_google_api do # NOTE: This Google API is a little different. It doesn't require an active edit. def upload_apk_to_internal_app_sharing(package_name, path_to_apk) end return result_upload.version_code end ) upload_source: path_to_apk current_edit.id, current_package_name, client.upload_edit_apk( result_upload = call_google_api do ensure_active_edit! def upload_apk(path_to_apk) end end ) listing language, current_edit.id, current_package_name, client.update_edit_listing( call_google_api do ) video: video short_description: short_description, full_description: full_description, title: title, language: language, listing = AndroidPublisher::Listing.new( ensure_active_edit! def update_listing_for_language(language: nil, title: nil, short_description: nil, full_description: nil, video: nil) # Updates or creates the listing for the specified language ##################################################### # @!group Modifying data ##################################################### end } ) download_dest: destination generated_universal_apk.download_id, generated_universal_apk.version_code, generated_universal_apk.package_name, client.download_generatedapk( call_google_api { def download_generated_universal_apk(generated_universal_apk:, destination:) # # IO stream or filename to receive content download # @param [IO, String] destination # The GeneratedUniversalApk object retrieved from a call to `list_generated_univeral_apks` # @param [Supply::GeneratedUniversalApk] generated_universal_apk # # Download a Universal APK generated by Google for a particular version code end end GeneratedUniversalApk.new(package_name, version_code, row.certificate_sha256_hash, row.generated_universal_apk.download_id) result.generated_apks.map do |row| result = call_google_api { client.list_generatedapks(package_name, version_code) } def list_generated_universal_apks(package_name:, version_code:) # # @return [Array] # @raise [FastlaneError] (Google Api Error) If no APK was found for the provided `package_name` and `version_code` # Version code of the app bundle. # @param [Fixnum] version_code # # Get the list of Universal APKs generated by Google from the AAB for a particular version code end return latest_versionad--z+!D r h ^ ] 5 - , V . -  _ E D + Z 7    T ( jL21%W hH(rqX.U4(  }|"-, # @param [String] language Localization code (a BCP-47 language tag; for example, "de-AT" for Austrian German). # @param [String] image_type Typically one of the elements of either Supply::IMAGES_TYPES or Supply::SCREENSHOT_TYPES # # Fetch all the images of a given type and for a given language of your store listing ##################################################### # @!group Screenshots ##################################################### end end ) ) file_size: file_size references_version: references_version, AndroidPublisher::ExpansionFile.new( expansion_file_type, apk_version_code, current_edit.id, current_package_name, client.update_edit_expansionfile( call_google_api do ensure_active_edit! def update_obb(apk_version_code, expansion_file_type, references_version, file_size) end end ) track track_name, self.current_edit.id, current_package_name, client.update_edit_track( call_google_api do ensure_active_edit! def upload_changelogs(track, track_name) end end raise return [] if e.status_code == 404 && e.to_s.include?("trackEmpty") rescue Google::Apis::ClientError => e return result.releases || [] ) track current_edit.id, current_package_name, result = client.get_edit_track( begin ensure_active_edit! def track_releases(track) # Get list of release names for track end end raise return [] if e.status_code == 404 && (e.to_s.include?("trackEmpty") || e.to_s.include?("Track not found")) rescue Google::Apis::ClientError => e return result.releases.flat_map(&:version_codes) || [] ) track current_edit.id, current_package_name, result = client.get_edit_track( begin ensure_active_edit! def track_version_codes(track) # Get list of version codes for track end end ) track_object track_name, current_edit.id, current_package_name, client.update_edit_track( call_google_api do ensure_active_edit! def update_track(track_name, track_object) end return all_tracks end all_tracks = all_tracks.select { |track| tracknames.include?(track.track) } if tracknames.length > 0 all_tracks = [] unless all_tracks all_tracks = call_google_api { client.list_edit_tracks(current_package_name, current_edit.id) }.tracks ensure_active_edit! def tracks(*tracknames) # Get a list of all tracks - returns the list end return result_upload.download_url end ) content_type: "application/octet-stream" upload_source: path_to_aab, package_name, client.uploadbundle_internalappsharingartifact( result_upload = call_google_api do # NOTE: This Google API is a little different. It doesn't require an active edit. def upload_bundle_to_internal_app_sharing(package_name, path_to_aab) end return result_upload.version_code end ) ack_bundle_installation_warning: Supply.config[:ack_bundle_installation_warning] content_type: "application/octet-stream", upload_source: path_to_aab, self.current_edit.id,ad*lS+*h3 A y o -   x p o 5 7 6 s X > = T5rU;:!aU'jR<;(N. {[7vPHG  # @!group Getting data ##################################################### end self.current_package_name = nil self.current_edit = nil end end end raise else ) changes_not_sent_for_review: true current_edit.id, current_package_name, client.commit_edit( elsif message.include?("Please set the query parameter changesNotSentForReview to true") ) current_edit.id current_package_name, client.commit_edit( if message.include?("The query parameter changesNotSentForReview must not be set") end message = e.body else message = error["error"] && error["error"]["message"] if error end nil rescue JSON.parse(e.body) error = begin end raise unless Supply.config[:rescue_changes_not_sent_for_review] rescue Google::Apis::ClientError => e ) changes_not_sent_for_review: Supply.config[:changes_not_sent_for_review] current_edit.id, current_package_name, client.commit_edit( begin call_google_api do ensure_active_edit! def commit_current_edit! # Commits the current edit saving all pending changes on Google Play end call_google_api { client.validate_edit(current_package_name, current_edit.id) } ensure_active_edit! def validate_current_edit! # Validates the current edit - does not change data on Google Play end self.current_package_name = nil self.current_edit = nil call_google_api { client.delete_edit(current_package_name, current_edit.id) } ensure_active_edit! def abort_current_edit # Aborts the current edit deleting all pending changes end self.current_package_name = package_name self.current_edit = call_google_api { client.insert_edit(package_name) } UI.user_error!("You currently have an active edit") if @current_edit def begin_edit(package_name: nil) # Begin modifying a certain package ##################################################### # @!group Handling the edit lifecycle ##################################################### end end UI.user_error!("No authentication parameters were specified. These must be provided in order to authenticate with Google") else service_account_json service_account_json = StringIO.new(JSON.dump(cred_json)) } client_email: params[:issuer] private_key: key.to_s, cred_json = { key = Google::APIClient::KeyUtils.load_from_pkcs12(File.expand_path(params[:key]), 'notasecret') UI.important("This type of authentication is deprecated. Please consider using JSON authentication instead") require 'google/api_client/auth/key_utils' elsif params[:key] && params[:issuer] super(params: params) if params[:json_key] || params[:json_key_data] def self.service_account_authentication(params: nil) ##################################################### # @!group Login ##################################################### attr_accessor :current_package_name # Package name of the currently edited element attr_accessor :current_edit # Reference to the entry we're currently editing. Might be nil if don't have one openad5 NM5 # Editing s # Editing something SCOPE = AndroidPublisher::AUTH_ANDROIDP # Editing something SCOPE = AndroidPublisher::AUTH_ANDROIDPUBLISHER SERVICE = AndroidPublisher::AndroidPublisherService class Client < AbstractGoogleServiceClient end end end