Skip to content

Commit 3f7f66c

Browse files
authored
Use generated API clients (#347)
* use generated API client for upload (reverts commit c313e1c) * check/use cached Firebase CLI credential before Application Default Credentials * fix upload performance * more progress messages * update version to 0.8.0
1 parent ca2ef36 commit 3f7f66c

22 files changed

+237
-356
lines changed

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ruby 2.7.6
1+
ruby 3.0.6

fastlane-plugin-firebase_app_distribution.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
1919
spec.require_paths = ['lib']
2020

2121
spec.add_dependency('google-apis-firebaseappdistribution_v1', '~> 0.3.0')
22+
spec.add_dependency('google-apis-firebaseappdistribution_v1alpha', '~> 0.2.0')
2223

2324
spec.add_development_dependency('pry')
2425
spec.add_development_dependency('bundler')

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

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,23 @@ def self.run(params)
3737

3838
# TODO(lkellogg): This sets the send timeout for all POST requests made by the client, but
3939
# ideally the timeout should only apply to the binary upload
40-
client = init_client(params[:service_credentials_file],
41-
params[:firebase_cli_token],
42-
params[:debug],
43-
timeout)
40+
client = init_v1_client(params[:service_credentials_file],
41+
params[:firebase_cli_token],
42+
params[:debug],
43+
timeout)
4444

45-
# If binary is an AAB, get the AAB info for this app, which includes the integration state and certificate data
45+
# If binary is an AAB, get the AAB info for this app, which includes the integration state
46+
# and certificate data
4647
if binary_type == :AAB
4748
aab_info = get_aab_info(client, app_name)
4849
validate_aab_setup!(aab_info)
4950
end
5051

5152
binary_type = binary_type_from_path(binary_path)
5253
UI.message("⌛ Uploading the #{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)
54+
operation = upload_binary(app_name, binary_path, client, timeout)
55+
UI.message("🕵️ Validating upload.")
56+
release = poll_upload_release_operation(client, operation, binary_type)
6857

6958
if binary_type == :AAB && aab_info && !aab_certs_included?(aab_info.test_certificate)
7059
updated_aab_info = get_aab_info(client, app_name)
@@ -86,6 +75,7 @@ def self.run(params)
8675
release.release_notes = Google::Apis::FirebaseappdistributionV1::GoogleFirebaseAppdistroV1ReleaseNotes.new(
8776
text: release_notes
8877
)
78+
UI.message("📜 Setting release notes.")
8979
release = update_release(client, release)
9080
end
9181

@@ -98,6 +88,7 @@ def self.run(params)
9888
tester_emails: emails,
9989
group_aliases: group_aliases
10090
)
91+
UI.message("📦 Distributing release.")
10192
distribute_release(client, release, request)
10293
else
10394
UI.message("⏩ No testers or groups passed in. Skipping this step.")
@@ -222,8 +213,8 @@ def self.release_notes(params)
222213
release_notes_param || Actions.lane_context[SharedValues::FL_CHANGELOG]
223214
end
224215

225-
def self.poll_upload_release_operation(client, operation_name, binary_type)
226-
operation = client.get_project_app_release_operation(operation_name)
216+
def self.poll_upload_release_operation(client, operation, binary_type)
217+
operation = client.get_project_app_release_operation(operation.name)
227218
MAX_POLLING_RETRIES.times do
228219
if operation.done && operation.response && operation.response['release']
229220
release = extract_release(operation)
@@ -257,6 +248,30 @@ def self.poll_upload_release_operation(client, operation_name, binary_type)
257248
extract_release(operation)
258249
end
259250

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class FirebaseAppDistributionAddTestersAction < Action
1111
extend Helper::FirebaseAppDistributionHelper
1212

1313
def self.run(params)
14-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14+
client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
1515

1616
if blank?(params[:emails]) && blank?(params[:file])
1717
UI.user_error!("Must specify `emails` or `file`.")

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ class FirebaseAppDistributionCreateGroupAction < Action
1111
extend Auth::FirebaseAppDistributionAuthClient
1212
extend Helper::FirebaseAppDistributionHelper
1313

14-
FirebaseAppDistributionV1 = Google::Apis::FirebaseappdistributionV1
15-
1614
def self.run(params)
1715
if blank?(params[:alias])
1816
UI.user_error!("Must specify `alias`.")
@@ -22,7 +20,7 @@ def self.run(params)
2220
UI.user_error!("Must specify `display_name`.")
2321
end
2422

25-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
23+
client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
2624

2725
project_number = params[:project_number]
2826
group_alias = params[:alias]
@@ -31,7 +29,7 @@ def self.run(params)
3129
UI.message("⏳ Creating tester group '#{group_alias} (#{display_name})' in project #{project_number}...")
3230

3331
parent = project_name(project_number)
34-
group = FirebaseAppDistributionV1::GoogleFirebaseAppdistroV1Group.new(
32+
group = Google::Apis::FirebaseappdistributionV1::GoogleFirebaseAppdistroV1Group.new(
3533
name: group_name(project_number, group_alias),
3634
display_name: display_name
3735
)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class FirebaseAppDistributionDeleteGroupAction < Action
1111
extend Helper::FirebaseAppDistributionHelper
1212

1313
def self.run(params)
14-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14+
client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
1515

1616
if blank?(params[:alias])
1717
UI.user_error!("Must specify `alias`.")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class FirebaseAppDistributionGetLatestReleaseAction < Action
1313
extend Helper::FirebaseAppDistributionHelper
1414

1515
def self.run(params)
16-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
16+
client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
1717

1818
UI.message("⏳ Fetching latest release for app #{params[:app]}...")
1919

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

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
require 'googleauth'
55
require_relative '../helper/firebase_app_distribution_helper'
66
require_relative '../helper/firebase_app_distribution_error_message'
7-
require_relative '../client/firebase_app_distribution_api_client'
87
require_relative '../helper/firebase_app_distribution_auth_client'
98

109
module Fastlane
@@ -14,11 +13,17 @@ class FirebaseAppDistributionGetUdidsAction < Action
1413
extend Helper::FirebaseAppDistributionHelper
1514

1615
def self.run(params)
17-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
18-
fad_api_client = Client::FirebaseAppDistributionApiClient.new(client.authorization.access_token, params[:debug])
16+
client = init_v1alpha_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
1917

20-
app_id = params[:app]
21-
udids = fad_api_client.get_udids(app_id)
18+
project_number = params[:project_number]
19+
if blank?(project_number)
20+
app_id = params[:app]
21+
if blank?(app_id)
22+
UI.user_error!("Must specify `project_number`.")
23+
end
24+
project_number = project_number_from_app_id(app_id)
25+
end
26+
udids = client.get_project_tester_udids(project_name(project_number)).tester_udids
2227

2328
if udids.empty?
2429
UI.important("App Distribution fetched 0 tester UDIDs. Nothing written to output file.")
@@ -32,7 +37,7 @@ def self.write_udids_to_file(udids, output_file)
3237
File.open(output_file, 'w') do |f|
3338
f.write("Device ID\tDevice Name\tDevice Platform\n")
3439
udids.each do |tester_udid|
35-
f.write("#{tester_udid[:udid]}\t#{tester_udid[:name]}\t#{tester_udid[:platform]}\n")
40+
f.write("#{tester_udid.udid}\t#{tester_udid.name}\t#{tester_udid.platform}\n")
3641
end
3742
end
3843
end
@@ -52,10 +57,18 @@ def self.details
5257

5358
def self.available_options
5459
[
60+
FastlaneCore::ConfigItem.new(key: :project_number,
61+
conflicting_options: [:app],
62+
env_name: "FIREBASEAPPDISTRO_PROJECT_NUMBER",
63+
description: "Your Firebase project number. You can find the project number in the Firebase console, on the General Settings page",
64+
type: Integer,
65+
optional: true),
5566
FastlaneCore::ConfigItem.new(key: :app,
67+
conflicting_options: [:project_number],
5668
env_name: "FIREBASEAPPDISTRO_APP",
5769
description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
58-
optional: false,
70+
optional: true,
71+
deprecated: "Use project_number (FIREBASEAPPDISTRO_PROJECT_NUMBER) instead",
5972
type: String),
6073
FastlaneCore::ConfigItem.new(key: :output_file,
6174
env_name: "FIREBASEAPPDISTRO_OUTPUT_FILE",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class FirebaseAppDistributionRemoveTestersAction < Action
1111
extend Helper::FirebaseAppDistributionHelper
1212

1313
def self.run(params)
14-
client = init_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14+
client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
1515

1616
if blank?(params[:emails]) && blank?(params[:file])
1717
UI.user_error!("Must specify `emails` or `file`.")

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

Lines changed: 0 additions & 97 deletions
This file was deleted.

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,12 @@ 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-
# 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)
4841
elsif (refresh_token = refresh_token_from_firebase_tools)
49-
UI.message("🔐 No authentication method found. Using cached Firebase CLI credentials.")
42+
UI.message("🔐 Authenticating with cached Firebase CLI credentials")
5043
firebase_token(refresh_token, debug)
44+
elsif !application_default_creds.nil?
45+
UI.message("🔐 Authenticating with Application Default Credentials")
46+
application_default_creds
5147
else
5248
UI.user_error!(ErrorMessage::MISSING_CREDENTIALS)
5349
nil
@@ -56,6 +52,12 @@ def get_authorization(google_service_path, firebase_cli_token, debug = false)
5652

5753
private
5854

55+
def application_default_creds
56+
Google::Auth.get_application_default([SCOPE])
57+
rescue
58+
nil
59+
end
60+
5961
def refresh_token_from_firebase_tools
6062
config_path = format_config_path
6163
if File.exist?(config_path)

0 commit comments

Comments
 (0)