Skip to content

Commit 267f503

Browse files
authored
Merge pull request #336 from firebase/lk/revert-uploads
Use old API client for uploads and bump to 0.7.2.pre.1
2 parents 65ca804 + ef69959 commit 267f503

File tree

6 files changed

+76
-52
lines changed

6 files changed

+76
-52
lines changed

lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,29 @@ def self.run(params)
4242
params[:debug],
4343
timeout)
4444

45-
# If binary is an AAB, get the AAB info for this app, which includes the integration state
46-
# and certificate data
45+
# If binary is an AAB, get the AAB info for this app, which includes the integration state and certificate data
4746
if binary_type == :AAB
4847
aab_info = get_aab_info(client, app_name)
4948
validate_aab_setup!(aab_info)
5049
end
5150

5251
binary_type = binary_type_from_path(binary_path)
5352
UI.message("⌛ Uploading the #{binary_type}.")
54-
operation = upload_binary(app_name, binary_path, client, timeout)
55-
release = poll_upload_release_operation(client, operation, binary_type)
53+
54+
# For some reason calling the client.upload_medium returns nil when
55+
# it should return a long running operation object
56+
# (https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-firebaseappdistribution_v1/lib/google/apis/firebaseappdistribution_v1/service.rb#L79).
57+
# We could use client.http, but is much slower
58+
# (https://github.com/firebase/fastlane-plugin-firebase_app_distribution/issues/330),
59+
# so we still use the old client for now.
60+
# TODO(kbolay) Prefer client.upload_medium, assuming it is sufficiently fast
61+
fad_api_client = Client::FirebaseAppDistributionApiClient.new(client.authorization.access_token, params[:debug])
62+
operation_name = fad_api_client.upload_binary(app_name,
63+
binary_path,
64+
platform.to_s,
65+
get_upload_timeout(params))
66+
67+
release = poll_upload_release_operation(client, operation_name, binary_type)
5668

5769
if binary_type == :AAB && aab_info && !aab_certs_included?(aab_info.test_certificate)
5870
updated_aab_info = get_aab_info(client, app_name)
@@ -210,8 +222,8 @@ def self.release_notes(params)
210222
release_notes_param || Actions.lane_context[SharedValues::FL_CHANGELOG]
211223
end
212224

213-
def self.poll_upload_release_operation(client, operation, binary_type)
214-
operation = client.get_project_app_release_operation(operation.name)
225+
def self.poll_upload_release_operation(client, operation_name, binary_type)
226+
operation = client.get_project_app_release_operation(operation_name)
215227
MAX_POLLING_RETRIES.times do
216228
if operation.done && operation.response && operation.response['release']
217229
release = extract_release(operation)
@@ -245,30 +257,6 @@ def self.poll_upload_release_operation(client, operation, binary_type)
245257
extract_release(operation)
246258
end
247259

248-
def self.upload_binary(app_name, binary_path, client, timeout)
249-
options = Google::Apis::RequestOptions.new
250-
options.max_elapsed_time = timeout # includes retries (default = no retries)
251-
options.header = {
252-
'Content-Type' => 'application/octet-stream',
253-
'X-Goog-Upload-File-Name' => File.basename(binary_path),
254-
'X-Goog-Upload-Protocol' => 'raw'
255-
}
256-
257-
# For some reason calling the client.upload_medium returns nil when
258-
# it should return a long running operation object, so we make a
259-
# standard http call instead and convert it to a long running object
260-
# https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-firebaseappdistribution_v1/lib/google/apis/firebaseappdistribution_v1/service.rb#L79
261-
# TODO(kbolay) Prefer client.upload_medium
262-
response = client.http(
263-
:post,
264-
"https://firebaseappdistribution.googleapis.com/upload/v1/#{app_name}/releases:upload",
265-
body: File.open(binary_path, 'rb').read,
266-
options: options
267-
)
268-
269-
Google::Apis::FirebaseappdistributionV1::GoogleLongrunningOperation.from_json(response)
270-
end
271-
272260
def self.extract_release(operation)
273261
Google::Apis::FirebaseappdistributionV1::GoogleFirebaseAppdistroV1Release.from_json(operation.response['release'].to_json)
274262
end

lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,46 @@ class FirebaseAppDistributionApiClient
77
include Helper::FirebaseAppDistributionHelper
88

99
BASE_URL = "https://firebaseappdistribution.googleapis.com"
10+
MAX_POLLING_RETRIES = 60
11+
POLLING_INTERVAL_SECONDS = 5
1012

1113
AUTHORIZATION = "Authorization"
14+
CONTENT_TYPE = "Content-Type"
15+
APPLICATION_OCTET_STREAM = "application/octet-stream"
1216
CLIENT_VERSION = "X-Client-Version"
1317

1418
def initialize(auth_token, debug = false)
1519
@auth_token = auth_token
1620
@debug = debug
1721
end
1822

23+
# Uploads the app binary to the Firebase API
24+
#
25+
# args
26+
# app_name - Firebase App resource name
27+
# binary_path - Absolute path to your app's aab/apk/ipa file
28+
# platform - 'android' or 'ios'
29+
# timeout - The amount of seconds before the upload will timeout, if not completed
30+
#
31+
# Returns the long-running operation name.
32+
#
33+
# Throws a user_error if the binary file does not exist
34+
def upload_binary(app_name, binary_path, platform, timeout)
35+
response = connection.post(binary_upload_url(app_name), read_binary(binary_path)) do |request|
36+
request.options.timeout = timeout # seconds
37+
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
38+
request.headers[CONTENT_TYPE] = APPLICATION_OCTET_STREAM
39+
request.headers[CLIENT_VERSION] = client_version_header_value
40+
request.headers["X-Goog-Upload-File-Name"] = File.basename(binary_path)
41+
request.headers["X-Goog-Upload-Protocol"] = "raw"
42+
end
43+
44+
response.body[:name] || ''
45+
rescue Errno::ENOENT # Raised when binary_path file does not exist
46+
binary_type = binary_type_from_path(binary_path)
47+
UI.user_error!("#{ErrorMessage.binary_not_found(binary_type)}: #{binary_path}")
48+
end
49+
1950
# Get tester UDIDs
2051
#
2152
# args
@@ -40,6 +71,10 @@ def client_version_header_value
4071
"fastlane/#{Fastlane::FirebaseAppDistribution::VERSION}"
4172
end
4273

74+
def binary_upload_url(app_name)
75+
"/upload/v1/#{app_name}/releases:upload"
76+
end
77+
4378
def get_udids_url(app_id)
4479
"/v1alpha/apps/#{app_id}/testers:getTesterUdids"
4580
end
@@ -52,6 +87,11 @@ def connection
5287
conn.adapter(Faraday.default_adapter)
5388
end
5489
end
90+
91+
def read_binary(path)
92+
# File must be read in binary mode to work on Windows
93+
File.open(path, 'rb').read
94+
end
5595
end
5696
end
5797
end

lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ def get_authorization(google_service_path, firebase_cli_token, debug = false)
3838
elsif !ENV["FIREBASE_TOKEN"].nil? && !ENV["FIREBASE_TOKEN"].empty?
3939
UI.message("🔐 Authenticating with FIREBASE_TOKEN environment variable")
4040
firebase_token(ENV["FIREBASE_TOKEN"], debug)
41-
elsif !application_default_creds.nil?
42-
UI.message("🔐 Authenticating with Application Default Credentials")
43-
application_default_creds
41+
# TODO(lkellogg): Not using Google::Auth.get_application_default yet while we are still
42+
# using the old client for uploads. ADC also does not work for the get_udids action:
43+
# https://cloud.google.com/docs/authentication/troubleshoot-adc#user-creds-client-based
44+
# For now go back to just using the environment variable:
45+
elsif !ENV["GOOGLE_APPLICATION_CREDENTIALS"].nil? && !ENV["GOOGLE_APPLICATION_CREDENTIALS"].empty?
46+
UI.message("🔐 Authenticating with GOOGLE_APPLICATION_CREDENTIALS environment variable: #{ENV['GOOGLE_APPLICATION_CREDENTIALS']}")
47+
service_account(ENV["GOOGLE_APPLICATION_CREDENTIALS"], debug)
4448
elsif (refresh_token = refresh_token_from_firebase_tools)
4549
UI.message("🔐 No authentication method found. Using cached Firebase CLI credentials.")
4650
firebase_token(refresh_token, debug)
@@ -52,12 +56,6 @@ def get_authorization(google_service_path, firebase_cli_token, debug = false)
5256

5357
private
5458

55-
def application_default_creds
56-
Google::Auth.get_application_default([SCOPE])
57-
rescue
58-
nil
59-
end
60-
6159
def refresh_token_from_firebase_tools
6260
config_path = format_config_path
6361
if File.exist?(config_path)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Fastlane
22
module FirebaseAppDistribution
3-
VERSION = "0.7.1"
3+
VERSION = "0.7.2.pre.1"
44
end
55
end

spec/firebase_app_distribution_action_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,9 @@ def stub_get_aab_info(integration_state = 'INTEGRATED')
318318

319319
it 'crashes if it exceeds polling threshold' do
320320
stub_const('Fastlane::Actions::FirebaseAppDistributionAction::MAX_POLLING_RETRIES', 0)
321-
allow_any_instance_of(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService)
322-
.to receive(:http)
323-
.and_return({ name: 'operation-name' }.to_json)
321+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient)
322+
.to receive(:upload_binary)
323+
.and_return('operation-name')
324324
allow_any_instance_of(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService)
325325
.to receive(:get_project_app_release_operation)
326326
.with('operation-name')
@@ -340,9 +340,9 @@ def stub_get_aab_info(integration_state = 'INTEGRATED')
340340
let(:release) { { name: "release-name", displayVersion: 'display-version' } }
341341

342342
before do
343-
allow_any_instance_of(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService)
344-
.to receive(:http)
345-
.and_return({ name: 'operation-name', result: release }.to_json)
343+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient)
344+
.to receive(:upload_binary)
345+
.and_return('operation-name')
346346
allow_any_instance_of(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService)
347347
.to receive(:get_project_app_release_operation)
348348
.and_return(Google::Apis::FirebaseappdistributionV1::GoogleLongrunningOperation.new(

spec/firebase_app_distribution_auth_client_spec.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44
let(:fake_binary_contents) { double("Contents") }
55
let(:firebase_auth) { Signet::OAuth2::Client }
66
let(:service_auth) { Google::Auth::ServiceAccountCredentials }
7-
let(:application_default_auth) { Google::Auth }
87
let(:fake_firebase_tools_contents) { "{\"tokens\": {\"refresh_token\": \"refresh_token\"} }" }
98
let(:fake_firebase_tools_contents_no_tokens_field) { "{}" }
109
let(:fake_firebase_tools_contents_no_refresh_field) { "{\"tokens\": \"empty\"}" }
1110
let(:fake_firebase_tools_contents_invalid_json) { "\"tokens\": \"empty\"}" }
1211
let(:fake_service_creds) { double("service_account_creds") }
1312
let(:fake_oauth_client) { double("oauth_client") }
14-
let(:fake_application_default_creds) { double("application_default_creds") }
1513
let(:payload) { { "access_token" => "service_fake_auth_token" } }
1614
let(:fake_error_response) { double("error_response") }
1715

@@ -59,9 +57,11 @@
5957
end
6058

6159
it 'auths with service credentials environment variable' do
62-
allow(application_default_auth).to receive(:get_application_default).and_return(fake_application_default_creds)
60+
allow(ENV).to receive(:[])
61+
.with("GOOGLE_APPLICATION_CREDENTIALS")
62+
.and_return("google_service_path")
6363
expect(auth_client.get_authorization(empty_val, empty_val))
64-
.to eq(fake_application_default_creds)
64+
.to eq(fake_service_creds)
6565
end
6666

6767
it 'auths with firebase token parameter' do
@@ -121,8 +121,6 @@
121121
end
122122

123123
describe 'when using cached firebase tools json file' do
124-
before { allow(application_default_auth).to receive(:get_application_default).and_return(nil) }
125-
126124
it 'authenticates' do
127125
allow(File).to receive(:read)
128126
.and_return(fake_firebase_tools_contents)

0 commit comments

Comments
 (0)