fastlane-plugin-firebase_app_distribution 0.8.0 → 0.9.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb +187 -39
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_add_testers_action.rb +8 -2
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_create_group_action.rb +7 -2
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_delete_group_action.rb +8 -2
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb +7 -1
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_udids.rb +8 -1
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_remove_testers_action.rb +8 -2
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb +21 -9
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb +15 -15
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb +3 -18
- data/lib/fastlane/plugin/firebase_app_distribution/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02dd5d9fe833cb902ea3f91b4124d4ba8ee7add6036e57da2cd16c6a79f7e503
|
4
|
+
data.tar.gz: b3c4d2740c8ab4a800554dae193068ffeea1d914f526005779be8792997a6d36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74bd1a05693eccb0c711208af34d8ae2d48bf3ed2cc623b754eb284c8337f6d78280583587e0ac5aaf892726ad9e266b160563eb8ae26df1fd4e4540b1c34dcc
|
7
|
+
data.tar.gz: d6de7b9d5154851e14ebba73a95ca98e2fab0bbf6895220715f5ee5ee089bb0cf4cdf5968f4d47b4c83b38dfcb7cde86228c250cfd68e1296e42685b0c1cc553
|
data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fastlane/action'
|
|
2
2
|
require 'open3'
|
3
3
|
require 'shellwords'
|
4
4
|
require 'googleauth'
|
5
|
+
require 'google/apis/firebaseappdistribution_v1'
|
5
6
|
require_relative '../helper/firebase_app_distribution_helper'
|
6
7
|
require_relative '../helper/firebase_app_distribution_error_message'
|
7
8
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
@@ -19,8 +20,10 @@ module Fastlane
|
|
19
20
|
extend Helper::FirebaseAppDistributionHelper
|
20
21
|
|
21
22
|
DEFAULT_UPLOAD_TIMEOUT_SECONDS = 300
|
22
|
-
|
23
|
-
|
23
|
+
UPLOAD_MAX_POLLING_RETRIES = 60
|
24
|
+
UPLOAD_POLLING_INTERVAL_SECONDS = 5
|
25
|
+
TEST_MAX_POLLING_RETRIES = 25
|
26
|
+
TEST_POLLING_INTERVAL_SECONDS = 30
|
24
27
|
|
25
28
|
def self.run(params)
|
26
29
|
params.values # to validate all inputs before looking for the ipa/apk/aab
|
@@ -31,16 +34,18 @@ module Fastlane
|
|
31
34
|
timeout = get_upload_timeout(params)
|
32
35
|
|
33
36
|
binary_path = get_binary_path(platform, params)
|
34
|
-
UI.user_error!("Couldn't find binary") if binary_path.nil?
|
35
|
-
UI.user_error!("Couldn't find binary at path #{binary_path}") unless File.exist?(binary_path)
|
37
|
+
UI.user_error!("Couldn't find binary.") if binary_path.nil?
|
38
|
+
UI.user_error!("Couldn't find binary at path #{binary_path}.") unless File.exist?(binary_path)
|
36
39
|
binary_type = binary_type_from_path(binary_path)
|
37
40
|
|
38
41
|
# TODO(lkellogg): This sets the send timeout for all POST requests made by the client, but
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
# ideally the timeout should only apply to the binary upload
|
43
|
+
init_google_api_client(params[:debug], timeout)
|
44
|
+
authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
45
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
46
|
+
client.authorization = authorization
|
47
|
+
alpha_client = Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new
|
48
|
+
alpha_client.authorization = authorization
|
44
49
|
|
45
50
|
# If binary is an AAB, get the AAB info for this app, which includes the integration state
|
46
51
|
# and certificate data
|
@@ -50,9 +55,9 @@ module Fastlane
|
|
50
55
|
end
|
51
56
|
|
52
57
|
binary_type = binary_type_from_path(binary_path)
|
53
|
-
UI.message("
|
58
|
+
UI.message("📡 Uploading the #{binary_type}.")
|
54
59
|
operation = upload_binary(app_name, binary_path, client, timeout)
|
55
|
-
UI.message("🕵️ Validating upload
|
60
|
+
UI.message("🕵️ Validating upload…")
|
56
61
|
release = poll_upload_release_operation(client, operation, binary_type)
|
57
62
|
|
58
63
|
if binary_type == :AAB && aab_info && !aab_certs_included?(aab_info.test_certificate)
|
@@ -79,6 +84,16 @@ module Fastlane
|
|
79
84
|
release = update_release(client, release)
|
80
85
|
end
|
81
86
|
|
87
|
+
test_devices =
|
88
|
+
get_value_from_value_or_file(params[:test_devices], params[:test_devices_file])
|
89
|
+
if present?(test_devices)
|
90
|
+
UI.message("🤖 Starting automated tests.")
|
91
|
+
release_test = test_release(alpha_client, release, test_devices, params[:test_username], params[:test_password], params[:test_username_resource], params[:test_password_resource])
|
92
|
+
unless params[:test_async]
|
93
|
+
poll_test_finished(alpha_client, release_test.name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
82
97
|
testers = get_value_from_value_or_file(params[:testers], params[:testers_file])
|
83
98
|
groups = get_value_from_value_or_file(params[:groups], params[:groups_file])
|
84
99
|
emails = string_to_array(testers)
|
@@ -214,24 +229,22 @@ module Fastlane
|
|
214
229
|
end
|
215
230
|
|
216
231
|
def self.poll_upload_release_operation(client, operation, binary_type)
|
217
|
-
|
218
|
-
|
232
|
+
UPLOAD_MAX_POLLING_RETRIES.times do
|
233
|
+
sleep(UPLOAD_POLLING_INTERVAL_SECONDS)
|
234
|
+
operation = client.get_project_app_release_operation(operation.name)
|
219
235
|
if operation.done && operation.response && operation.response['release']
|
220
236
|
release = extract_release(operation)
|
221
|
-
|
222
|
-
|
237
|
+
case operation.response['result']
|
238
|
+
when 'RELEASE_UPDATED'
|
223
239
|
UI.success("✅ Uploaded #{binary_type} successfully; updated provisioning profile of existing release #{release_version(release)}.")
|
224
|
-
|
225
|
-
elsif result == 'RELEASE_UNMODIFIED'
|
240
|
+
when 'RELEASE_UNMODIFIED'
|
226
241
|
UI.success("✅ The same #{binary_type} was found in release #{release_version(release)} with no changes, skipping.")
|
227
|
-
break
|
228
242
|
else
|
229
243
|
UI.success("✅ Uploaded #{binary_type} successfully and created release #{release_version(release)}.")
|
230
244
|
end
|
231
245
|
break
|
232
246
|
elsif !operation.done
|
233
|
-
|
234
|
-
operation = client.get_project_app_release_operation(operation.name)
|
247
|
+
next
|
235
248
|
else
|
236
249
|
if operation.error && operation.error.message
|
237
250
|
UI.user_error!("#{ErrorMessage.upload_binary_error(binary_type)}: #{operation.error.message}")
|
@@ -261,7 +274,7 @@ module Fastlane
|
|
261
274
|
# it should return a long running operation object, so we make a
|
262
275
|
# standard http call instead and convert it to a long running object
|
263
276
|
# 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
|
277
|
+
# TODO(kbolay): Prefer client.upload_medium
|
265
278
|
response = client.http(
|
266
279
|
:post,
|
267
280
|
"https://firebaseappdistribution.googleapis.com/upload/v1/#{app_name}/releases:upload",
|
@@ -319,6 +332,99 @@ module Fastlane
|
|
319
332
|
end
|
320
333
|
end
|
321
334
|
|
335
|
+
def self.test_release(alpha_client, release, test_devices, username = nil, password = nil, username_resource = nil, password_resource = nil)
|
336
|
+
if username_resource.nil? ^ password_resource.nil?
|
337
|
+
UI.user_error!("Username and password resource names for automated tests need to be specified together.")
|
338
|
+
end
|
339
|
+
field_hints = nil
|
340
|
+
if !username_resource.nil? && !password_resource.nil?
|
341
|
+
field_hints =
|
342
|
+
Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaLoginCredentialFieldHints.new(
|
343
|
+
username_resource_name: username_resource,
|
344
|
+
password_resource_name: password_resource
|
345
|
+
)
|
346
|
+
end
|
347
|
+
|
348
|
+
if username.nil? ^ password.nil?
|
349
|
+
UI.user_error!("Username and password for automated tests need to be specified together.")
|
350
|
+
end
|
351
|
+
login_credential = nil
|
352
|
+
if !username.nil? && !password.nil?
|
353
|
+
login_credential =
|
354
|
+
Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaLoginCredential.new(
|
355
|
+
username: username,
|
356
|
+
password: password,
|
357
|
+
field_hints: field_hints
|
358
|
+
)
|
359
|
+
else
|
360
|
+
unless field_hints.nil?
|
361
|
+
UI.user_error!("Must specify username and password for automated tests if resource names are set.")
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
device_executions = string_to_array(test_devices, ';').map do |td_string|
|
366
|
+
td_hash = parse_test_device_string(td_string)
|
367
|
+
Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaDeviceExecution.new(
|
368
|
+
device: Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaTestDevice.new(
|
369
|
+
model: td_hash['model'],
|
370
|
+
version: td_hash['version'],
|
371
|
+
orientation: td_hash['orientation'],
|
372
|
+
locale: td_hash['locale']
|
373
|
+
)
|
374
|
+
)
|
375
|
+
end
|
376
|
+
|
377
|
+
release_test =
|
378
|
+
Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaReleaseTest.new(
|
379
|
+
login_credential: login_credential,
|
380
|
+
device_executions: device_executions
|
381
|
+
)
|
382
|
+
alpha_client.create_project_app_release_test(release.name, release_test)
|
383
|
+
rescue Google::Apis::Error => err
|
384
|
+
UI.crash!(err)
|
385
|
+
end
|
386
|
+
|
387
|
+
def self.poll_test_finished(alpha_client, release_test_name)
|
388
|
+
TEST_MAX_POLLING_RETRIES.times do
|
389
|
+
UI.message("⏳ Waiting for test(s) to complete…")
|
390
|
+
sleep(TEST_POLLING_INTERVAL_SECONDS)
|
391
|
+
release_test = alpha_client.get_project_app_release_test(release_test_name)
|
392
|
+
if release_test.device_executions.all? { |e| e.state == 'PASSED' }
|
393
|
+
UI.success("✅ Passed automated test(s).")
|
394
|
+
return
|
395
|
+
end
|
396
|
+
release_test.device_executions.each do |de|
|
397
|
+
case de.state
|
398
|
+
when 'PASSED', 'IN_PROGRESS'
|
399
|
+
next
|
400
|
+
when 'FAILED'
|
401
|
+
UI.test_failure!("Automated test failed for #{device_to_s(de.device)}: #{de.failed_reason}.")
|
402
|
+
when 'INCONCLUSIVE'
|
403
|
+
UI.test_failure!("Automated test inconclusive for #{device_to_s(de.device)}: #{de.inconclusive_reason}.")
|
404
|
+
else
|
405
|
+
UI.test_failure!("Unsupported automated test state for #{device_to_s(de.device)}: #{de.state}.")
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
UI.test_failure!("Tests are running longer than expected.")
|
410
|
+
end
|
411
|
+
|
412
|
+
def self.parse_test_device_string(td_string)
|
413
|
+
allowed_keys = %w[model version locale orientation]
|
414
|
+
key_value_pairs = td_string.split(',').map do |key_value_string|
|
415
|
+
key, value = key_value_string.split('=')
|
416
|
+
unless allowed_keys.include?(key)
|
417
|
+
UI.user_error!("Unrecognized key in test_devices. Can only contain keys #{allowed_keys.join(', ')}.")
|
418
|
+
end
|
419
|
+
[key, value]
|
420
|
+
end
|
421
|
+
Hash[key_value_pairs]
|
422
|
+
end
|
423
|
+
|
424
|
+
def self.device_to_s(device)
|
425
|
+
"#{device.model} (#{device.version}/#{device.orientation}/#{device.locale})"
|
426
|
+
end
|
427
|
+
|
322
428
|
def self.available_options
|
323
429
|
[
|
324
430
|
# iOS Specific
|
@@ -350,7 +456,7 @@ module Fastlane
|
|
350
456
|
verify_block: proc do |value|
|
351
457
|
UI.user_error!("firebase_app_distribution: '#{value}' is not a valid value for android_artifact_type. Should be 'APK' or 'AAB'") unless ['APK', 'AAB'].include?(value)
|
352
458
|
end),
|
353
|
-
#
|
459
|
+
# General
|
354
460
|
FastlaneCore::ConfigItem.new(key: :app,
|
355
461
|
env_name: "FIREBASEAPPDISTRO_APP",
|
356
462
|
description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
|
@@ -359,26 +465,38 @@ module Fastlane
|
|
359
465
|
FastlaneCore::ConfigItem.new(key: :firebase_cli_path,
|
360
466
|
deprecated: "This plugin no longer uses the Firebase CLI",
|
361
467
|
env_name: "FIREBASEAPPDISTRO_FIREBASE_CLI_PATH",
|
362
|
-
description: "
|
468
|
+
description: "Absolute path of the Firebase CLI command",
|
363
469
|
type: String),
|
470
|
+
FastlaneCore::ConfigItem.new(key: :debug,
|
471
|
+
description: "Print verbose debug output",
|
472
|
+
optional: true,
|
473
|
+
default_value: false,
|
474
|
+
type: Boolean),
|
475
|
+
|
476
|
+
# Release Distribution
|
477
|
+
FastlaneCore::ConfigItem.new(key: :upload_timeout,
|
478
|
+
description: "Amount of seconds before the upload will timeout, if not completed",
|
479
|
+
optional: true,
|
480
|
+
default_value: DEFAULT_UPLOAD_TIMEOUT_SECONDS,
|
481
|
+
type: Integer),
|
364
482
|
FastlaneCore::ConfigItem.new(key: :groups,
|
365
483
|
env_name: "FIREBASEAPPDISTRO_GROUPS",
|
366
|
-
description: "
|
484
|
+
description: "Group aliases used for distribution, separated by commas",
|
367
485
|
optional: true,
|
368
486
|
type: String),
|
369
487
|
FastlaneCore::ConfigItem.new(key: :groups_file,
|
370
488
|
env_name: "FIREBASEAPPDISTRO_GROUPS_FILE",
|
371
|
-
description: "
|
489
|
+
description: "Path to file containing group aliases used for distribution, separated by commas",
|
372
490
|
optional: true,
|
373
491
|
type: String),
|
374
492
|
FastlaneCore::ConfigItem.new(key: :testers,
|
375
493
|
env_name: "FIREBASEAPPDISTRO_TESTERS",
|
376
|
-
description: "
|
494
|
+
description: "Email addresses of testers, separated by commas",
|
377
495
|
optional: true,
|
378
496
|
type: String),
|
379
497
|
FastlaneCore::ConfigItem.new(key: :testers_file,
|
380
498
|
env_name: "FIREBASEAPPDISTRO_TESTERS_FILE",
|
381
|
-
description: "
|
499
|
+
description: "Path to file containing email addresses of testers, separated by commas",
|
382
500
|
optional: true,
|
383
501
|
type: String),
|
384
502
|
FastlaneCore::ConfigItem.new(key: :release_notes,
|
@@ -388,27 +506,56 @@ module Fastlane
|
|
388
506
|
type: String),
|
389
507
|
FastlaneCore::ConfigItem.new(key: :release_notes_file,
|
390
508
|
env_name: "FIREBASEAPPDISTRO_RELEASE_NOTES_FILE",
|
391
|
-
description: "
|
509
|
+
description: "Path to file containing release notes for this build",
|
392
510
|
optional: true,
|
393
511
|
type: String),
|
394
|
-
|
395
|
-
|
512
|
+
|
513
|
+
# Release Testing
|
514
|
+
FastlaneCore::ConfigItem.new(key: :test_devices,
|
515
|
+
env_name: "FIREBASEAPPDISTRO_TEST_DEVICES",
|
516
|
+
description: "List of devices to run automated tests on, in the format 'model=<model-id>,version=<os-version-id>,locale=<locale>,orientation=<orientation>;model=<model-id>,...'. Run 'gcloud firebase test android|ios models list' to see available devices",
|
396
517
|
optional: true,
|
397
518
|
type: String),
|
398
|
-
FastlaneCore::ConfigItem.new(key: :
|
399
|
-
|
519
|
+
FastlaneCore::ConfigItem.new(key: :test_devices_file,
|
520
|
+
env_name: "FIREBASEAPPDISTRO_TEST_DEVICES_FILE",
|
521
|
+
description: "Path to file containing a list of devices to run automated tests on, in the format 'model=<model-id>,version=<os-version-id>,locale=<locale>,orientation=<orientation>;model=<model-id>,...'. Run 'gcloud firebase test android|ios models list' to see available devices",
|
522
|
+
optional: true,
|
523
|
+
type: String),
|
524
|
+
FastlaneCore::ConfigItem.new(key: :test_username,
|
525
|
+
description: "Username for automatic login",
|
400
526
|
optional: true,
|
527
|
+
type: String),
|
528
|
+
FastlaneCore::ConfigItem.new(key: :test_password,
|
529
|
+
description: "Password for automatic login",
|
530
|
+
optional: true,
|
531
|
+
type: String),
|
532
|
+
FastlaneCore::ConfigItem.new(key: :test_username_resource,
|
533
|
+
description: "Resource name of the username field for automatic login",
|
534
|
+
optional: true,
|
535
|
+
type: String),
|
536
|
+
FastlaneCore::ConfigItem.new(key: :test_password_resource,
|
537
|
+
description: "Resource name of the password field for automatic login",
|
538
|
+
optional: true,
|
539
|
+
type: String),
|
540
|
+
FastlaneCore::ConfigItem.new(key: :test_async,
|
541
|
+
description: "Don't wait for automatic test results",
|
542
|
+
optional: false,
|
401
543
|
default_value: false,
|
402
|
-
|
544
|
+
type: Boolean),
|
545
|
+
|
546
|
+
# Auth
|
547
|
+
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
|
548
|
+
description: "Auth token generated using the Firebase CLI's login:ci command",
|
549
|
+
optional: true,
|
550
|
+
type: String),
|
403
551
|
FastlaneCore::ConfigItem.new(key: :service_credentials_file,
|
404
|
-
description: "Path to Google service account json",
|
552
|
+
description: "Path to Google service account json file",
|
405
553
|
optional: true,
|
406
554
|
type: String),
|
407
|
-
FastlaneCore::ConfigItem.new(key: :
|
408
|
-
description: "
|
555
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
556
|
+
description: "Google service account json file content",
|
409
557
|
optional: true,
|
410
|
-
|
411
|
-
type: Integer)
|
558
|
+
type: String)
|
412
559
|
]
|
413
560
|
end
|
414
561
|
|
@@ -425,7 +572,8 @@ module Fastlane
|
|
425
572
|
<<-CODE
|
426
573
|
firebase_app_distribution(
|
427
574
|
app: "<your Firebase app ID>",
|
428
|
-
testers: "snatchev@google.com, rebeccahe@google.com"
|
575
|
+
testers: "snatchev@google.com, rebeccahe@google.com",
|
576
|
+
test_devices: "model=shiba,version=34,locale=en,orientation=portrait;model=b0q,version=33,locale=en,orientation=portrait",
|
429
577
|
)
|
430
578
|
CODE
|
431
579
|
]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'fastlane/action'
|
2
2
|
require 'fastlane_core/ui/ui'
|
3
|
-
|
3
|
+
require 'google/apis/firebaseappdistribution_v1'
|
4
4
|
require_relative '../helper/firebase_app_distribution_helper'
|
5
5
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
6
6
|
|
@@ -11,7 +11,9 @@ module Fastlane
|
|
11
11
|
extend Helper::FirebaseAppDistributionHelper
|
12
12
|
|
13
13
|
def self.run(params)
|
14
|
-
|
14
|
+
init_google_api_client(params[:debug])
|
15
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
16
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
15
17
|
|
16
18
|
if blank?(params[:emails]) && blank?(params[:file])
|
17
19
|
UI.user_error!("Must specify `emails` or `file`.")
|
@@ -78,6 +80,10 @@ module Fastlane
|
|
78
80
|
description: "Path to Google service credentials file",
|
79
81
|
optional: true,
|
80
82
|
type: String),
|
83
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
84
|
+
description: "Google service account json file content",
|
85
|
+
optional: true,
|
86
|
+
type: String),
|
81
87
|
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
|
82
88
|
description: "Auth token generated using the Firebase CLI's login:ci command",
|
83
89
|
optional: true,
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'fastlane/action'
|
2
2
|
require 'fastlane_core/ui/ui'
|
3
3
|
require 'google/apis/firebaseappdistribution_v1'
|
4
|
-
|
5
4
|
require_relative '../helper/firebase_app_distribution_helper'
|
6
5
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
7
6
|
|
@@ -20,7 +19,9 @@ module Fastlane
|
|
20
19
|
UI.user_error!("Must specify `display_name`.")
|
21
20
|
end
|
22
21
|
|
23
|
-
|
22
|
+
init_google_api_client(params[:debug])
|
23
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
24
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
24
25
|
|
25
26
|
project_number = params[:project_number]
|
26
27
|
group_alias = params[:alias]
|
@@ -86,6 +87,10 @@ module Fastlane
|
|
86
87
|
description: "Path to Google service credentials file",
|
87
88
|
optional: true,
|
88
89
|
type: String),
|
90
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
91
|
+
description: "Google service account json file content",
|
92
|
+
optional: true,
|
93
|
+
type: String),
|
89
94
|
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
|
90
95
|
description: "Auth token generated using the Firebase CLI's login:ci command",
|
91
96
|
optional: true,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'fastlane/action'
|
2
2
|
require 'fastlane_core/ui/ui'
|
3
|
-
|
3
|
+
require 'google/apis/firebaseappdistribution_v1'
|
4
4
|
require_relative '../helper/firebase_app_distribution_helper'
|
5
5
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
6
6
|
|
@@ -11,7 +11,9 @@ module Fastlane
|
|
11
11
|
extend Helper::FirebaseAppDistributionHelper
|
12
12
|
|
13
13
|
def self.run(params)
|
14
|
-
|
14
|
+
init_google_api_client(params[:debug])
|
15
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
16
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
15
17
|
|
16
18
|
if blank?(params[:alias])
|
17
19
|
UI.user_error!("Must specify `alias`.")
|
@@ -65,6 +67,10 @@ module Fastlane
|
|
65
67
|
description: "Path to Google service credentials file",
|
66
68
|
optional: true,
|
67
69
|
type: String),
|
70
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
71
|
+
description: "Google service account json file content",
|
72
|
+
optional: true,
|
73
|
+
type: String),
|
68
74
|
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
|
69
75
|
description: "Auth token generated using the Firebase CLI's login:ci command",
|
70
76
|
optional: true,
|
@@ -13,7 +13,9 @@ module Fastlane
|
|
13
13
|
extend Helper::FirebaseAppDistributionHelper
|
14
14
|
|
15
15
|
def self.run(params)
|
16
|
-
|
16
|
+
init_google_api_client(params[:debug])
|
17
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
18
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
17
19
|
|
18
20
|
UI.message("⏳ Fetching latest release for app #{params[:app]}...")
|
19
21
|
|
@@ -89,6 +91,10 @@ module Fastlane
|
|
89
91
|
description: "Path to Google service account json",
|
90
92
|
optional: true,
|
91
93
|
type: String),
|
94
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
95
|
+
description: "Google service account json file content",
|
96
|
+
optional: true,
|
97
|
+
type: String),
|
92
98
|
FastlaneCore::ConfigItem.new(key: :debug,
|
93
99
|
description: "Print verbose debug output",
|
94
100
|
optional: true,
|
data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_udids.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fastlane/action'
|
|
2
2
|
require 'open3'
|
3
3
|
require 'shellwords'
|
4
4
|
require 'googleauth'
|
5
|
+
require 'google/apis/firebaseappdistribution_v1alpha'
|
5
6
|
require_relative '../helper/firebase_app_distribution_helper'
|
6
7
|
require_relative '../helper/firebase_app_distribution_error_message'
|
7
8
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
@@ -13,7 +14,9 @@ module Fastlane
|
|
13
14
|
extend Helper::FirebaseAppDistributionHelper
|
14
15
|
|
15
16
|
def self.run(params)
|
16
|
-
|
17
|
+
init_google_api_client(params[:debug])
|
18
|
+
client = Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new
|
19
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
17
20
|
|
18
21
|
project_number = params[:project_number]
|
19
22
|
if blank?(project_number)
|
@@ -83,6 +86,10 @@ module Fastlane
|
|
83
86
|
description: "Path to Google service account json",
|
84
87
|
optional: true,
|
85
88
|
type: String),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
90
|
+
description: "Google service account json file content",
|
91
|
+
optional: true,
|
92
|
+
type: String),
|
86
93
|
FastlaneCore::ConfigItem.new(key: :debug,
|
87
94
|
description: "Print verbose debug output",
|
88
95
|
optional: true,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'fastlane/action'
|
2
2
|
require 'fastlane_core/ui/ui'
|
3
|
-
|
3
|
+
require 'google/apis/firebaseappdistribution_v1'
|
4
4
|
require_relative '../helper/firebase_app_distribution_helper'
|
5
5
|
require_relative '../helper/firebase_app_distribution_auth_client'
|
6
6
|
|
@@ -11,7 +11,9 @@ module Fastlane
|
|
11
11
|
extend Helper::FirebaseAppDistributionHelper
|
12
12
|
|
13
13
|
def self.run(params)
|
14
|
-
|
14
|
+
init_google_api_client(params[:debug])
|
15
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
16
|
+
client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
|
15
17
|
|
16
18
|
if blank?(params[:emails]) && blank?(params[:file])
|
17
19
|
UI.user_error!("Must specify `emails` or `file`.")
|
@@ -73,6 +75,10 @@ module Fastlane
|
|
73
75
|
description: "Path to Google service credentials file",
|
74
76
|
optional: true,
|
75
77
|
type: String),
|
78
|
+
FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
|
79
|
+
description: "Google service account json file content",
|
80
|
+
optional: true,
|
81
|
+
type: String),
|
76
82
|
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
|
77
83
|
description: "Auth token generated using the Firebase CLI's login:ci command",
|
78
84
|
optional: true,
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
CHANGED
@@ -20,6 +20,7 @@ module Fastlane
|
|
20
20
|
# auth method is used, unset all other auth variables/parameters to nil/empty
|
21
21
|
#
|
22
22
|
# args
|
23
|
+
# google_service_json_data - Google service account json file content as a string
|
23
24
|
# google_service_path - Absolute path to the Google service account file
|
24
25
|
# firebase_cli_token - Refresh token
|
25
26
|
# debug - Whether to enable debug-level logging
|
@@ -28,10 +29,13 @@ module Fastlane
|
|
28
29
|
# FIREBASE_TOKEN - see firebase_cli_token
|
29
30
|
#
|
30
31
|
# Crashes if given invalid or missing credentials
|
31
|
-
def get_authorization(google_service_path, firebase_cli_token, debug = false)
|
32
|
+
def get_authorization(google_service_path, firebase_cli_token, google_service_json_data, debug = false)
|
32
33
|
if !google_service_path.nil? && !google_service_path.empty?
|
33
34
|
UI.message("🔐 Authenticating with --service_credentials_file path parameter: #{google_service_path}")
|
34
|
-
|
35
|
+
service_account_from_file(google_service_path, debug)
|
36
|
+
elsif !google_service_json_data.nil? && !google_service_json_data.empty?
|
37
|
+
UI.message("🔐 Authenticating with --service_credentials_json content parameter")
|
38
|
+
service_account_from_json(google_service_json_data, debug)
|
35
39
|
elsif !firebase_cli_token.nil? && !firebase_cli_token.empty?
|
36
40
|
UI.message("🔐 Authenticating with --firebase_cli_token parameter")
|
37
41
|
firebase_token(firebase_cli_token, debug)
|
@@ -100,24 +104,32 @@ module Fastlane
|
|
100
104
|
UI.user_error!(error_message)
|
101
105
|
end
|
102
106
|
|
103
|
-
def
|
107
|
+
def service_account_from_json(google_service_json_data, debug)
|
108
|
+
get_service_account_credentials(google_service_json_data, debug)
|
109
|
+
end
|
110
|
+
|
111
|
+
def service_account_from_file(google_service_path, debug)
|
112
|
+
get_service_account_credentials(File.read(google_service_path), debug)
|
113
|
+
rescue Errno::ENOENT
|
114
|
+
UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_service_account_credentials(json_data, debug)
|
118
|
+
json_file = JSON.parse(json_data)
|
104
119
|
# check if it's an external account or service account
|
105
|
-
json_file = JSON.parse(File.read(google_service_path))
|
106
120
|
auth = json_file["type"] == "external_account" ? Google::Auth::ExternalAccount::Credentials : Google::Auth::ServiceAccountCredentials
|
107
121
|
service_account_credentials = auth.make_creds(
|
108
|
-
json_key_io:
|
122
|
+
json_key_io: StringIO.new(json_data),
|
109
123
|
scope: SCOPE
|
110
124
|
)
|
111
125
|
service_account_credentials.fetch_access_token!
|
112
126
|
service_account_credentials
|
113
|
-
rescue Errno::ENOENT
|
114
|
-
UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
|
115
127
|
rescue Signet::AuthorizationError => error
|
116
|
-
error_message = "#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}:
|
128
|
+
error_message = "#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}: "
|
117
129
|
if debug
|
118
130
|
error_message += "\n#{error_details(error)}"
|
119
131
|
else
|
120
|
-
error_message +=
|
132
|
+
error_message += debug_instructions.to_s
|
121
133
|
end
|
122
134
|
UI.user_error!(error_message)
|
123
135
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
CHANGED
@@ -1,46 +1,46 @@
|
|
1
1
|
module ErrorMessage
|
2
2
|
MISSING_CREDENTIALS = "Missing authentication credentials. Set up Application Default Credentials, your Firebase refresh token, or sign in with the Firebase CLI, and try again."
|
3
|
-
MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again"
|
4
|
-
SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again"
|
5
|
-
PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file"
|
6
|
-
PARSE_FIREBASE_TOOLS_JSON_ERROR = "Encountered error parsing json file. Ensure the firebase-tools.json file is formatted correctly"
|
7
|
-
UPLOAD_RELEASE_NOTES_ERROR = "App Distribution halted because it had a problem uploading release notes"
|
8
|
-
UPLOAD_TESTERS_ERROR = "App Distribution halted because it had a problem adding testers/groups"
|
9
|
-
GET_RELEASE_TIMEOUT = "App Distribution failed to fetch release information"
|
3
|
+
MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again."
|
4
|
+
SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again."
|
5
|
+
PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file."
|
6
|
+
PARSE_FIREBASE_TOOLS_JSON_ERROR = "Encountered error parsing json file. Ensure the firebase-tools.json file is formatted correctly."
|
7
|
+
UPLOAD_RELEASE_NOTES_ERROR = "App Distribution halted because it had a problem uploading release notes."
|
8
|
+
UPLOAD_TESTERS_ERROR = "App Distribution halted because it had a problem adding testers/groups."
|
9
|
+
GET_RELEASE_TIMEOUT = "App Distribution failed to fetch release information."
|
10
10
|
REFRESH_TOKEN_ERROR = "App Distribution could not generate credentials from the refresh token specified."
|
11
|
-
APP_NOT_ONBOARDED_ERROR = "App Distribution not onboarded"
|
11
|
+
APP_NOT_ONBOARDED_ERROR = "App Distribution not onboarded."
|
12
12
|
INVALID_APP_ID = "App Distribution could not find your app. Make sure to onboard your app by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution. App ID"
|
13
13
|
INVALID_PROJECT = "App Distribution could not find your Firebase project. Make sure to onboard an app in your project by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution."
|
14
14
|
INVALID_PATH = "Could not read content from"
|
15
15
|
INVALID_TESTERS = "Could not enable access for testers. Check that the tester emails are formatted correctly, the groups exist and you are using group aliases (not group names) for specifying groups."
|
16
16
|
INVALID_TESTER_GROUP = "App Distribution could not find your tester group. Make sure that it exists before trying to add testers, and that the group alias is specified correctly."
|
17
17
|
INVALID_TESTER_GROUP_NAME = "The tester group name should be 4-63 characters, and valid characters are /[a-z][0-9]-/."
|
18
|
-
INVALID_RELEASE_NOTES = "Failed to set release notes"
|
19
|
-
SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified"
|
18
|
+
INVALID_RELEASE_NOTES = "Failed to set release notes."
|
19
|
+
SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified."
|
20
20
|
PLAY_ACCOUNT_NOT_LINKED = "This project is not linked to a Google Play account."
|
21
21
|
APP_NOT_PUBLISHED = "This app is not published in the Google Play console."
|
22
22
|
NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT = "App with matching package name does not exist in Google Play."
|
23
23
|
PLAY_IAS_TERMS_NOT_ACCEPTED = "You must accept the Play Internal App Sharing (IAS) terms to upload AABs."
|
24
24
|
INVALID_EMAIL_ADDRESS = "You passed an invalid email address."
|
25
|
-
TESTER_LIMIT_VIOLATION = "Creating testers would exceed tester limit"
|
25
|
+
TESTER_LIMIT_VIOLATION = "Creating testers would exceed tester limit."
|
26
26
|
|
27
27
|
def self.aab_upload_error(aab_state)
|
28
28
|
"Failed to process the AAB: #{aab_state}"
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.binary_not_found(binary_type)
|
32
|
-
"Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}"
|
32
|
+
"Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}."
|
33
33
|
end
|
34
34
|
|
35
35
|
def self.parse_binary_metadata_error(binary_type)
|
36
|
-
"Failed to extract #{binary_type} metadata from the #{binary_type} path"
|
36
|
+
"Failed to extract #{binary_type} metadata from the #{binary_type} path."
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.upload_binary_error(binary_type)
|
40
|
-
"App Distribution halted because it had a problem uploading the #{binary_type}"
|
40
|
+
"App Distribution halted because it had a problem uploading the #{binary_type}."
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.binary_processing_error(binary_type)
|
44
|
-
"App Distribution failed to process the #{binary_type}"
|
44
|
+
"App Distribution failed to process the #{binary_type}."
|
45
45
|
end
|
46
46
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'fastlane_core/ui/ui'
|
2
|
-
require 'google/apis/firebaseappdistribution_v1'
|
3
|
-
require 'google/apis/firebaseappdistribution_v1alpha'
|
4
2
|
require 'cfpropertylist'
|
5
3
|
|
6
4
|
module Fastlane
|
@@ -29,10 +27,10 @@ module Fastlane
|
|
29
27
|
|
30
28
|
# Returns the array representation of a string with trimmed comma
|
31
29
|
# seperated values.
|
32
|
-
def string_to_array(string)
|
30
|
+
def string_to_array(string, delimiter = ",")
|
33
31
|
return [] if string.nil?
|
34
32
|
# Strip string and then strip individual values
|
35
|
-
string.strip.split(
|
33
|
+
string.strip.split(delimiter).map(&:strip)
|
36
34
|
end
|
37
35
|
|
38
36
|
def parse_plist(path)
|
@@ -72,17 +70,7 @@ module Fastlane
|
|
72
70
|
"#{project_name(project_number)}/groups/#{group_alias}"
|
73
71
|
end
|
74
72
|
|
75
|
-
def
|
76
|
-
init_client(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new,
|
77
|
-
service_credentials_file, firebase_cli_token, debug, timeout)
|
78
|
-
end
|
79
|
-
|
80
|
-
def init_v1alpha_client(service_credentials_file, firebase_cli_token, debug, timeout = nil)
|
81
|
-
init_client(Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new,
|
82
|
-
service_credentials_file, firebase_cli_token, debug, timeout)
|
83
|
-
end
|
84
|
-
|
85
|
-
def init_client(client, service_credentials_file, firebase_cli_token, debug, timeout = nil)
|
73
|
+
def init_google_api_client(debug, timeout = nil)
|
86
74
|
if debug
|
87
75
|
UI.important("Warning: Debug logging enabled. Output may include sensitive information.")
|
88
76
|
Google::Apis.logger.level = Logger::DEBUG
|
@@ -93,9 +81,6 @@ module Fastlane
|
|
93
81
|
unless timeout.nil?
|
94
82
|
Google::Apis::ClientOptions.default.send_timeout_sec = timeout
|
95
83
|
end
|
96
|
-
|
97
|
-
client.authorization = get_authorization(service_credentials_file, firebase_cli_token, debug)
|
98
|
-
client
|
99
84
|
end
|
100
85
|
|
101
86
|
def deep_symbolize_keys(hash)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-firebase_app_distribution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Natchev
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-01-
|
13
|
+
date: 2024-01-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: google-apis-firebaseappdistribution_v1
|